Back to skill

Security audit

Env Guard

Security checks for vulnerabilities and agentic risk

Overview

This is a local secret scanner, but it can return unredacted secret lines and may follow symlinks outside the requested scan folder.

Review before installing or using in CI. Prefer CLI use only on trusted workspaces until the raw finding field is removed or fully redacted, and avoid scanning untrusted repositories unless symlink traversal is blocked. Do not serialize or upload programmatic reports from this version because they may contain plaintext secrets.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
src/env-guard.js:98
Finding
Programmatic Scan Results Retain Unredacted Secret-Bearing Source Lines<![CDATA[ ## Vulnerability Details **File Location**: `src/env-guard.js:98-106` **Vulnerability Type**: Plaintext sensitive-data exposure **Risk Level**: High ### Vulnerable Code ```js const finding = { file: filePath, line: i + 1, type: pat.name, severity: pat.severity, snippet: this._redact(match[0]), raw: line.trim().substring(0, 120) }; // Check allowlist const key = `${filePath}:${i + 1}:${pat.name}`; if (!this.allowlist.includes(key)) { this.findings.push(finding); } ``` ### Technical Analysis The detected match is redacted before being assigned to `snippet`, but the same finding also retains up to 120 characters of the original source line in `raw`. That source line can contain the complete credential, connection string, webhook URL, password, token, or neighboring sensitive values. The `EnvGuard` class is exported for programmatic use, and `report()` returns `this.findings` without removing `raw`. Therefore, callers that serialize, log, upload, or archive the returned report can unintentionally disclose the exact secrets that the scanner was intended to protect. The current CLI output only prints `snippet`, so direct CLI output does not expose `raw`. The vulnerability affects API consumers and any future output path that processes the complete report object. ### Attack Path 1. A scanned file contains a credential matching one of the configured patterns. 2. `_scanFile()` detects the credential and creates a finding. 3. `_redact()` protects only the `snippet` property. 4. The original line, including the credential, is copied into `finding.raw`. 5. `report()` returns the finding to the API consumer. 6. A CI integration, logging framework, report serializer, or other downstream consumer records the report. 7. Anyone with access to that downstream destination can recover the plaintext credential. ### Impact Assessment An attacker who can access generated reports, logs, telemetry, or CI artifacts may obtain credentials present in scan ...[truncated 421 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `raw` property from findings by default. 2. If source context is required, replace every detected sensitive span with a fixed placeholder before storing it. 3. Apply redaction to the complete line rather than only to the first matched value. 4. Consider reporting only the file path, line number, pattern type, severity, and redacted match. 5. If plaintext evidence is operationally necessary, require an explicit opt-in option with prominent documentation warning that the report contains secrets. 6. Add automated tests confirming that serialized findings never contain the original credential or adjacent sensitive values. 7. Review downstream CI and logging integrations and delete previously generated artifacts that may contain `raw` findings. A safer implementation should resemble: ```js const redactedLine = line.replace(pat.pattern, matchValue => this._redact(matchValue) ); const finding = { file: filePath, line: i + 1, type: pat.name, severity: pat.severity, snippet: this._redact(match[0]), context: redactedLine.trim().substring(0, 120) }; ``` For global or overlapping patterns, perform comprehensive redaction across all configured secret patterns before retaining context. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/env-guard.js:63
Finding
Repository-Controlled File Symlinks Can Escape the Requested Scan Root<![CDATA[ ## Vulnerability Details **File Location**: `src/env-guard.js:63-81` **Vulnerability Type**: Scan-root boundary bypass through symbolic links **Risk Level**: High ### Vulnerable Code ```js _scanDir(dirPath) { let entries; try { entries = fs.readdirSync(dirPath, { withFileTypes: true }); } catch { return; } for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); if (entry.isDirectory()) { if (!this.ignoreDirs.includes(entry.name)) { this._scanDir(fullPath); } continue; } // Skip binary/image files const ext = path.extname(entry.name).toLowerCase(); if (DEFAULT_IGNORE.some(i => i.startsWith('.') && ext === i)) continue; this._scanFile(fullPath); } } ``` The resulting path is subsequently opened without canonical-path validation: ```js _scanFile(filePath) { let content; try { content = fs.readFileSync(filePath, 'utf8'); } catch { return; } ``` ### Technical Analysis Directory entries that are not reported as directories are passed directly to `_scanFile()`. The code does not reject `entry.isSymbolicLink()` entries and does not compare the canonical target path against the canonical scan root. Node.js `fs.readFileSync()` follows a file symbolic link. Consequently, a symlink stored inside an attacker-controlled repository can point to a readable file outside the selected workspace. When a victim scans the repository, EnvGuard may read the external target even though the user requested a scan of only the repository. The process remains constrained by the operating-system permissions of the user running EnvGuard. It cannot read files that user cannot read. However, it violates the expected scan-root boundary and can expose matched content through findings. The risk is amplified by the separate unredacted `raw` field vulnerability. ### Attack Path 1. An attacker creates a repository containing a file symlink, such as `config-link`, targeting a likely ...[truncated 1420 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject symbolic links by default: ```js if (entry.isSymbolicLink()) { continue; } ``` 2. Record the canonical scan root when scanning begins: ```js const root = fs.realpathSync(targetPath); this.scanRoot = root; this._scanDir(root); ``` 3. Before opening any file, resolve its canonical path and verify that it remains below the canonical root: ```js const resolved = fs.realpathSync(filePath); const relative = path.relative(this.scanRoot, resolved); if ( relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative) ) { return; } ``` 4. Use the canonical path for containment checks, but consider reporting a safe root-relative display path rather than an external absolute path. 5. If symlink traversal is intentionally supported, make it an explicit opt-in option and document that linked targets may fall outside the workspace. 6. Add tests for file symlinks, nested symlinks, links to directories, broken links, and links whose lexical path is inside the root but whose canonical target is outside it. 7. Combine this remediation with removal or comprehensive redaction of the `raw` result field to prevent external file contents from being copied into reports. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Ae1

High
Category
analysis-evasion
Content
node src/env-guard.js scan ./my-project
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Scope Creep

Low
Category
Excessive Agency
Content
**USE AT YOUR OWN RISK.**

- The author(s) are NOT liable for any damages, losses, or consequences arising from 
  the use or misuse of this software — including but not limited to financial loss, 
  data loss, security breaches, business interruption, or any indirect/consequential damages.
- This software does NOT constitute financial, legal, trading, or professional advice.
- Users are solely responsible for evaluating whether this software is suitable for
Confidence
70% confidence
Finding
Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Scope Creep

Low
Category
Excessive Agency
Content
**USE AT YOUR OWN RISK.**

- The author(s) are NOT liable for any damages, losses, or consequences arising from 
  the use or misuse of this software — including but not limited to financial loss, 
  data loss, security breaches, business interruption, or any indirect/consequential damages.
- This software does NOT constitute financial, legal, trading, or professional advice.
- Users are solely responsible for evaluating whether this software is suitable for
Confidence
70% confidence
Finding
Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Static analysis

No suspicious patterns detected.