Back to skill

Security audit

env-manager

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its scaffolding purpose, but it contains under-disclosed ways to redirect or perform file writes outside the claimed workspace boundary.

Review before installing. This skill does not appear to exfiltrate data or execute commands itself, but it can create and overwrite scaffold files, and its current implementation has workspace-boundary weaknesses. Only use it in a disposable or trusted workspace, avoid setting ENV_MANAGER_WORKSPACE, and treat the exported generateScaffoldFiles API as unsafe unless fixed to enforce workspace containment and non-overwrite behavior.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
env-manager.js:63
Finding

Environment Variable Permits Writes Outside the Declared Workspace

Content
View full analysis

Vulnerability Details

File Location: env-manager.js:63-75
Vulnerability Type: Workspace boundary bypass through runtime path redirection
Risk Level: High

Vulnerable Code

js
// Resolution order:
//   1. ENV_MANAGER_WORKSPACE env var (explicit override, must be absolute)
//   2. Otherwise: the project root that contains this skill — i.e. the
//      grandparent of this file's directory (skills/env-manager/ -> repo root).
//      This is fixed and does not search upward beyond two levels.
function getWorkspace() {
  if (process.env.ENV_MANAGER_WORKSPACE) {
    const o = path.resolve(process.env.ENV_MANAGER_WORKSPACE);
    if (!path.isAbsolute(o)) {
      throw new Error('ENV_MANAGER_WORKSPACE must be an absolute path');
    }
    return o;
  }
  // Fixed: two levels up from this file — no upward walk.
  return path.resolve(__dirname, '..', '..');
}

Technical Analysis

The workspace root is taken from the process-controlled ENV_MANAGER_WORKSPACE environment variable. This value controls the base directory used for project scaffolding under environments/ and state storage under memory/environments/.

The absolute-path validation is ineffective because path.resolve() converts both relative and absolute input into an absolute path before path.isAbsolute() is called. Consequently, any non-empty override accepted by path.resolve() passes the check.

This behavior contradicts the trust model stated in SKILL.md, README.md, and SECURITY-AUDIT.md, which says that runtime path redirection is not supported and that writes remain inside the Agent workspace. The override is also unnecessary for the minimum privileges required by a workspace-scaffolding Skill.

Attack Path

  1. An attacker or untrusted launcher sets ENV_MANAGER_WORKSPACE to a writable directory outside the intended Agent workspace.
  2. The Skill is invoked through setupEnvironment() or the `--setup ...[truncated 1071 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove ENV_MANAGER_WORKSPACE support and derive the workspace from an immutable, trusted configuration.
  2. If an override is required, validate the original value before applying path.resolve():
    js
    const raw = process.env.ENV_MANAGER_WORKSPACE;
    if (!path.isAbsolute(raw)) {
      throw new Error('ENV_MANAGER_WORKSPACE must be an absolute path');
    }
    
  3. Define a trusted root independently of attacker-controlled environment variables.
  4. Canonicalize both the trusted root and requested destination with fs.realpathSync().
  5. Enforce containment using path.relative() and reject results that are .., begin with ../, or are absolute.
  6. Update all trust-model documentation to accurately describe any retained override.
  7. Add tests covering relative overrides, absolute external paths, path normalization, and symbolic-link escapes.

T09 · Insecure Skill Coding Practices

Error
Location
env-manager.js:352
Finding

Exported Scaffold Function Accepts an Arbitrary Write Directory

Content
View full analysis

Vulnerability Details

File Location: env-manager.js:352-354 and env-manager.js:878
Vulnerability Type: Public API bypass of workspace path restrictions
Risk Level: High

Vulnerable Code

js
function generateScaffoldFiles(type, envName, envDir) {
  ensureDir(envDir);

  switch (type) {

The function writes files by joining fixed scaffold names directly to the caller-provided directory, for example:

js
fs.writeFileSync(path.join(envDir, 'package.json'), JSON.stringify(pkg, null, 2));
fs.writeFileSync(path.join(envDir, 'index.js'),
  `// ${envName}\nconsole.log('${envName} running');\n`);
fs.writeFileSync(path.join(envDir, '.gitignore'), 'node_modules\n.env\n*.log\n');

It is exposed as a public API:

js
// File scaffolding (fs only)
generateScaffoldFiles,

Technical Analysis

generateScaffoldFiles() accepts envDir directly from its caller and does not verify that the supplied destination is under the intended workspace. It also does not call sanitizeEnvName() for direct API invocations.

The higher-level setupEnvironment() derives its destination from the workspace and a sanitized name, but callers can bypass those protections by invoking the exported low-level function directly. The API is explicitly documented in SKILL.md, making this a supported and discoverable path rather than an unreachable internal helper.

Depending on the selected type, the function creates or overwrites files such as package.json, index.js, .gitignore, Dockerfile, docker-compose.yml, main.go, Cargo.toml, src/main.rs, or .env.example.

Attack Path

  1. Code with access to the Skill module imports env-manager.js.
  2. It invokes an exported call such as:
    js
    generateScaffoldFiles('node', 'x', '/attacker-selected/writable/path');
    
  3. ensureDir() creates the target directory recursively if necessary.
  4. The scaffold generator writes ...[truncated 712 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove generateScaffoldFiles from module.exports and keep it as an internal implementation detail.
  2. Do not accept an arbitrary destination from public callers. Accept only a validated environment type and name, then derive the destination internally.
  3. Apply sanitizeEnvName() to every externally supplied environment name.
  4. Canonicalize the destination and verify that it remains under a trusted workspace root before creating directories or writing files.
  5. Refuse to overwrite existing files by default, or require an explicit trusted overwrite option.
  6. Add a preview or dry-run mode that reports the exact files that would be affected.
  7. Add tests proving that absolute paths, parent traversal, and destinations outside the workspace are rejected.

T09 · Insecure Skill Coding Practices

Warning
Location
env-manager.js:84
Finding

Symbolic Links Can Redirect Scaffold and State Writes Outside the Workspace

Content
View full analysis

Vulnerability Details

File Location: env-manager.js:84-102
Vulnerability Type: Symbolic-link path containment bypass
Risk Level: Medium

Vulnerable Code

js
function ensureDir(dir) {
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
}

function loadJSON(file, fallback) {
  try {
    const data = fs.readFileSync(file, 'utf8');
    return JSON.parse(data);
  } catch {
    return fallback || {};
  }
}

function saveJSON(file, data) {
  ensureDir(path.dirname(file));
  fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8');
}

Scaffold writes use the same unchecked path model:

js
function generateScaffoldFiles(type, envName, envDir) {
  ensureDir(envDir);

Technical Analysis

Paths are constructed lexically with path.join(), followed by existsSync(), recursive directory creation, and ordinary file writes. The implementation does not use lstat() to reject symbolic links and does not canonicalize the final parent directory with realpath() before writing.

Lexical containment is insufficient when a path component inside the workspace is a symbolic link. For example, a seemingly valid path such as <workspace>/environments/project/package.json can resolve to an external filesystem location if environments/project is a symlink.

The same condition applies to the state directory. If memory/environments is replaced with a symlink, environments.json, ports.json, and services.json reads and writes follow it.

Exploitation requires the attacker to be able to prepare or modify entries within the workspace before the Skill runs.

Attack Path

  1. An attacker with workspace write access creates a symbolic link:
    text
    <workspace>/environments/victim -> /external/writable/directory
    
    Alternatively, the attacker replaces <workspace>/memory/environments with a symlink.
  2. The attacker inv ...[truncated 925 chars]
Remediation
View remediation

Remediation Suggestions

  1. Inspect each existing path component with fs.lstatSync() and reject symbolic links before writing.
  2. Resolve the trusted workspace and destination parent using fs.realpathSync() or fs.realpathSync.native().
  3. Verify canonical containment with path.relative() immediately before each write.
  4. Open files using restrictive flags and modes. Use exclusive creation where overwriting is not required.
  5. Consider using directory file descriptors and platform-supported no-follow behavior to reduce time-of-check/time-of-use exposure.
  6. Fail closed when canonicalization or path inspection fails.
  7. Add tests for symlinks at environments, the individual environment directory, memory, and memory/environments.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The documented trust boundary says writes occur only inside the agent workspace, but the finding indicates behavior can be redirected via ENV_MANAGER_WORKSPACE. If true, this enables path redirection outside the expected workspace boundary, undermining the primary safety claim and potentially allowing unauthorized file creation or overwrite; additionally, undeclared lsof-based inspection commands expand operational capability beyond simple scaffolding and may cause callers to trust and execute more powerful actions than advertised.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 67)May include surrounding context.

md
const em = require('./env-manager.js');

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill claims its writes stay inside the agent workspace, but getWorkspace() honors ENV_MANAGER_WORKSPACE and accepts any absolute path. An attacker who can influence the environment can redirect scaffold and JSON writes to arbitrary filesystem locations, violating the skill boundary and potentially overwriting sensitive files or planting project artifacts outside the intended workspace.

Content

No source excerpt is available for this finding.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

Content

Scanner excerpt · README.md (reported line 7)May include surrounding context.

md
## What it does

For each language, it writes a few starter files into a project directory and returns commands the agent should run. The skill does not run any commands itself.

| Type   | Files written                                                            |
|--------|--------------------------------------------------------------------------|

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README makes explicit safety claims that the skill does not perform process control or use process-spawning behavior, yet the documented CLI includes --services --start/--stop/--status, which implies service lifecycle management. This inconsistency is dangerous because agents or reviewers may trust the metadata-only claim and grant the skill broader trust than warranted, creating a risk of hidden process control capability or unsafe delegation behavior.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documented service-control commands exceed the stated skill scope of tracking runtime state as metadata only. A mismatch between declared capability and advertised interface can mislead a calling agent into invoking operational controls that were not approved during review, undermining security assumptions around least privilege and tool behavior.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The inline documentation explicitly says there is no environment-variable-based path redirection, yet the code supports ENV_MANAGER_WORKSPACE. This trust-model mismatch is security-relevant because operators and calling agents may rely on the stated confinement guarantees while the implementation allows path redirection, increasing the chance of unsafe deployment and unnoticed boundary bypass.

Content

No source excerpt is available for this finding.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
60% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · env-manager.js (reported line 380)May include surrounding context.

js
const dockerfile = `FROM node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm install\nCOPY . .\nEXPOSE 3000\nCMD ["node", "index.js"]\n`;
      fs.writeFileSync(path.join(envDir, 'Dockerfile'), dockerfile);

      const compose = `version: '3.8'\nservices:\n  app:\n    build: .\n    ports:\n      - "3000:3000"\n    volumes:\n      - .:/app\n    environment:\n      - NODE_ENV=development\n`;
      fs.writeFileSync(path.join(envDir, 'docker-compose.yml'), compose);
      break;
    }

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code performs file-system modifications by creating directories, generating scaffold files, and persisting environment metadata. Although the header documents that the skill writes starter files, these safety-critical writes occur here without a local confirmation prompt or user-facing disclosure at the point of action, which can leave callers unaware that invoking setup mutates the workspace immediately.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.