Skip to content

Security review: 1 medium, 4 low findings #2

@yarikoptic

Description

@yarikoptic

Considering to use so did review using https://gist.github.com/2a491f29bf662d3e04fe1713b1757729.git and some concerns look legit, e.g. it is a constant battle against hardcoded /tmp paths in debian packages ;)

Vibecoder Security Review: DRYwall

Date: 2026-04-07

Summary

Found 1 medium-priority issue and 4 low-priority/informational issues in this Node.js MCP server plugin. The codebase is small (~230 lines of source), has no web-facing attack surface (no auth, no HTTP server, no user data), and uses execFile correctly to avoid shell injection. Overall security posture is reasonable for a CLI tool.

Findings

[MEDIUM] Arbitrary npm Package Execution via jscpdVersion Config

Location: src/lib.js:60-61

Issue: The jscpdVersion value from .drywallrc.json is interpolated into the npx package specifier without validation:

const fullArgs = [`jscpd@${version}`, ...args];
execFile("npx", fullArgs, ...);

A malicious .drywallrc.json could set jscpdVersion to a completely different package name (e.g., "jscpdVersion": "../../malicious-pkg"), though execFile mitigates the shell injection vector. The realistic risk is low since the attacker would need write access to the project's config file, but it violates the principle of validating external input before passing it to process execution.

Impact: If an attacker controls .drywallrc.json (e.g., via a malicious PR or compromised dependency that writes config), they could cause npx to execute an arbitrary npm package.

Suggested fix: Validate that jscpdVersion matches a semver pattern:

const VERSION_RE = /^\d+\.\d+\.\d+(-[\w.]+)?$/;
if (version && !VERSION_RE.test(version)) {
  throw new Error(`Invalid jscpdVersion: ${version}`);
}

[LOW] Predictable Temp Directory Path

Location: src/lib.js:7

Issue: Report output uses a fixed path /tmp/drywall-report:

export const REPORT_DIR = "/tmp/drywall-report";

Impact: On shared systems, another user could create a symlink at /tmp/drywall-report pointing elsewhere (symlink/TOCTOU attack). Practical risk is low since this is a developer CLI tool typically run on single-user machines, and jscpd writes a JSON file (not executable content).

Suggested fix: Use os.tmpdir() with a unique suffix (e.g., mkdtemp) if multi-user environments are a concern.


[LOW] Unrestricted Path Scanning

Location: src/jscpd.js:70

Issue: The path parameter from MCP tool input is passed directly to jscpd without path validation:

const targetPath = scanPath || config.path || ".";
args.push(targetPath);

Impact: An MCP client could request scanning of any directory readable by the process (e.g., /etc, ~/.ssh). The scan only detects duplication (no file contents are exfiltrated in full), but code fragments are returned in the response. This is by-design for an MCP tool (the LLM host already has file access), so this is informational.


[LOW] 4 High-Severity npm Audit Vulnerabilities (Transitive)

Issue: npm audit reports 4 high-severity vulnerabilities in transitive dependencies:

  • hono <=4.12.6 -- cookie injection, SSE injection, file access, prototype pollution
  • @hono/node-server <1.19.10 -- auth bypass in static paths
  • express-rate-limit 8.2.0-8.2.1 -- IPv4-mapped IPv6 bypass
  • path-to-regexp 8.0.0-8.3.0 -- ReDoS

Impact: These are transitive deps pulled by @modelcontextprotocol/sdk. DRYwall doesn't use Hono/Express directly -- it only uses the MCP SDK's stdio transport, so these vulnerabilities are not exploitable in this context. The server has no HTTP listener.

Suggested fix: npm audit fix to update, or document as accepted risk.


[INFO] Minimal .gitignore

Location: .gitignore

Issue: Only ignores node_modules/. Missing common patterns like .env, *.log, .DS_Store. Not a real risk since this is a plugin with no secrets, but worth noting for hygiene.


Not Applicable

The following checks found no issues (as expected for a non-web MCP server plugin):

  • Secrets & Keys -- No hardcoded credentials, API keys, or tokens found anywhere in source
  • Auth & Accounts -- No authentication system (MCP stdio transport, no HTTP)
  • User Data & Privacy -- No user data storage or retrieval
  • Test vs Production -- No debug backdoors, no test credentials
  • File Uploads -- No upload handling
  • XSS / SQL Injection -- No HTML rendering, no database
  • CORS / Security Headers -- No HTTP server
  • Prompt Injection -- No LLM integration in the server itself

Quick Wins

  1. Validate jscpdVersion against a semver regex before passing to npx
  2. Run npm audit fix to clear transitive vulnerability warnings

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions