Back to skill

Security audit

Hackathon Swarm Coding

Security checks for vulnerabilities and agentic risk

Overview

This code-generation skill explains its OpenRouter use and logging, but it can let generated output write files outside the intended project folder.

Install only in a throwaway or tightly isolated workspace with no valuable files nearby, and do not include secrets in prompts. Review generated code and logs before committing or sharing. The path traversal issue should be fixed before normal use because a model response or prompt injection could overwrite files outside the generated project directory.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
orchestrator.js:296
Finding
Arbitrary File Write Through Model-Controlled Path Traversal## Vulnerability Details **File Location**: `orchestrator.js`, lines 296–306 **Vulnerability Type**: Path traversal leading to arbitrary file creation or overwrite **Risk Level**: High ```js function parseWorkerOutput(output, roleDir) { const fileRegex = /=== FILE: (.+?)\s*===\n([\s\S]*?)\n=== END FILE ===/g; let match; const files = []; while ((match = fileRegex.exec(output)) !== null) { const filePath = match[1].replace(/^\.?\//, ''); const content = match[2]; const fullPath = path.join(roleDir, filePath); fs.mkdirSync(path.dirname(fullPath), { recursive: true }); fs.writeFileSync(fullPath, content); files.push(filePath); } if (files.length === 0) throw new Error('No file blocks found'); return files; } ``` ### Technical Analysis `parseWorkerOutput()` treats filenames supplied in an OpenRouter model response as trusted filesystem paths. The normalization only removes one leading slash or `./`; it does not reject `..` path components or confirm that the resolved destination remains beneath `roleDir`. In Node.js, `path.join(roleDir, "../../../../target")` resolves traversal components and can identify a destination outside the intended generated-project directory. The subsequent `fs.mkdirSync()` and `fs.writeFileSync()` calls then create or overwrite that destination using the privileges of the orchestrator process. Because worker output is influenced by the user's prompt and generated by an external model, it must be treated as untrusted input. Prompt injection or unexpected model behavior could produce a malicious file block even though the system prompt requests relative paths. Related planner-generated values, including role IDs and declared output paths, are also used in path construction without strict validation. This expands the untrusted path surface, although the direct arbitrary-write sink is shown above. ### Attack Path 1. An attacker supplies a crafted project prompt containing instructions intended to ...[truncated 1701 chars]
Remediation
## Remediation Suggestions 1. Canonicalize and validate every generated filename before performing any filesystem operation: ```js function safePathWithin(baseDir, untrustedPath) { if (typeof untrustedPath !== 'string' || untrustedPath.includes('\0')) { throw new Error('Invalid generated file path'); } if (path.isAbsolute(untrustedPath)) { throw new Error('Absolute paths are not permitted'); } const base = path.resolve(baseDir); const destination = path.resolve(base, untrustedPath); if (destination !== base && !destination.startsWith(base + path.sep)) { throw new Error('Generated file path escapes its assigned directory'); } return destination; } ``` 2. Replace the vulnerable path construction with the validated destination: ```js const filePath = match[1].trim(); const fullPath = safePathWithin(roleDir, filePath); fs.mkdirSync(path.dirname(fullPath), { recursive: true }); fs.writeFileSync(fullPath, content, { flag: 'wx' }); ``` Use an overwrite policy appropriate to the application; `flag: "wx"` prevents silent replacement of existing files. 3. Reject paths containing empty components, `.` or `..` components, drive prefixes, UNC paths, null bytes, and platform-specific separators where they are not expected. 4. Validate planner-generated role IDs and output paths against strict schemas. Role IDs should use a narrow pattern such as `^[a-z0-9-]+$`, and every output path should undergo the same containment check. 5. Apply limits to the number and size of generated files to reduce denial-of-service risks. 6. Run the orchestrator under a dedicated, least-privileged operating-system account in an isolated workspace or container. Mount only the intended output directory as writable. 7. Treat all external model responses as hostile data. Do not rely on prompt instructions such as “use relative paths” as a security boundary. 8. Add automated tests covering trave ...[truncated 289 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (17)

Credential Access

High
Category
Privilege Escalation
Content
const WORKSPACE_ROOT = path.resolve(__dirname, '..'); // workspace root

function loadEnv() {
  const envPath = path.join(WORKSPACE_ROOT, '.env');
  if (!fs.existsSync(envPath)) throw new Error('.env not found in workspace root. Add OPENROUTER_API_KEY.');
  const content = fs.readFileSync(envPath, 'utf8');
  const env = {};
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const WORKSPACE_ROOT = path.resolve(__dirname, '..'); // workspace root

function loadEnv() {
  const envPath = path.join(WORKSPACE_ROOT, '.env');
  if (!fs.existsSync(envPath)) throw new Error('.env not found in workspace root. Add OPENROUTER_API_KEY.');
  const content = fs.readFileSync(envPath, 'utf8');
  const env = {};
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
function loadEnv() {
  const envPath = path.join(WORKSPACE_ROOT, '.env');
  if (!fs.existsSync(envPath)) throw new Error('.env not found in workspace root. Add OPENROUTER_API_KEY.');
  const content = fs.readFileSync(envPath, 'utf8');
  const env = {};
  content.split('\n').forEach(line => {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
function loadEnv() {
  const envPath = path.join(WORKSPACE_ROOT, '.env');
  if (!fs.existsSync(envPath)) throw new Error('.env not found in workspace root. Add OPENROUTER_API_KEY.');
  const content = fs.readFileSync(envPath, 'utf8');
  const env = {};
  content.split('\n').forEach(line => {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
function loadEnv() {
  const envPath = path.join(WORKSPACE_ROOT, '.env');
  if (!fs.existsSync(envPath)) throw new Error('.env not found in workspace root. Add OPENROUTER_API_KEY.');
  const content = fs.readFileSync(envPath, 'utf8');
  const env = {};
  content.split('\n').forEach(line => {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Unvalidated Output Injection

High
Category
Output Handling
Content
const fileRegex = /=== FILE: (.+?)\s*===\n([\s\S]*?)\n=== END FILE ===/g;
  let match;
  const files = [];
  while ((match = fileRegex.exec(output)) !== null) {
    const filePath = match[1].replace(/^\.?\//, '');
    const content = match[2];
    const fullPath = path.join(roleDir, filePath);
Confidence
100% confidence
Finding
The parser trusts model-controlled file names and writes them to disk via path.join(roleDir, filePath) after only stripping a leading ./ or /. An LLM can emit paths containing ../ segments, allowing path traversal and arbitrary file overwrite outside the intended role directory, potentially clobbering project files, configs, or executable scripts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises capabilities that imply access to environment variables, network, and shell-like operations, but it does not declare an explicit tool scope or permissions boundary. In an autonomous multi-agent code generation skill, this increases the risk of overbroad execution, unexpected access to secrets in the parent workspace, and unreviewed external calls.

Ssd 3

Medium
Confidence
95% confidence
Finding
The skill explicitly warns that prompts and agent reasoning are stored in DECISIONS.md and .learnings/, meaning user input and internal reasoning may be persistently written to disk. Because this runs from the parent workspace and is autonomous, sensitive prompts, secrets accidentally included by users, or contextual business data can be retained in plain text and later exposed to other tools, users, or commits.

Ssd 3

Medium
Confidence
96% confidence
Finding
The continuous-improvement section instructs automatic capture of worker failures, better approaches, user corrections, and missing capabilities into persistent files. This creates a durable record of user-supplied content and execution context that may include proprietary code, credentials, incident details, or other sensitive material, especially in a code-generation workflow that encourages rich natural-language prompts.

Ssd 3

Medium
Confidence
97% confidence
Finding
The metadata explicitly states that project files and decision logs are retained across runs and that logs may contain user prompts and agent reasoning. Persisting raw prompts and reasoning creates a data leakage risk because sensitive user input, secrets, internal architecture, or proprietary requests may be stored on disk and later exposed to other users, agents, or processes.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The skill defines an automatic integration trigger based on broad natural-language keywords like 'blockchain', 'tokens', 'NFTs', and 'Privy'. This can cause wallet/authentication code to be inserted when the user did not explicitly request it, expanding scope and potentially introducing sensitive auth, smart-contract, or third-party integration code into generated projects unexpectedly.

Ssd 3

Medium
Confidence
95% confidence
Finding
The warnings acknowledge that DECISIONS.md and .learnings/ may capture sensitive prompts or architectural details, confirming that the skill is designed to preserve potentially private natural-language inputs. In an autonomous multi-agent coding system, these logs can accumulate high-value information over time and increase the blast radius of any workspace exposure or accidental sharing.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The orchestrator sends the full user prompt to OpenRouter, an external third-party API, without any consent gate, redaction, or prominent warning to the operator. In this skill context, prompts may contain proprietary source code, credentials, internal architecture, or other sensitive project details, so exfiltration risk is real even if this is part of intended functionality.

Ssd 3

Medium
Confidence
96% confidence
Finding
The orchestrator writes the raw user prompt into persistent artifacts like DECISIONS.md and workspace metadata, creating unnecessary long-term retention of potentially sensitive input. In an autonomous code-generation skill, users may provide internal requirements, tokens, URLs, or confidential business context that then become stored in plaintext across generated project folders.

Ssd 3

Medium
Confidence
97% confidence
Finding
Each worker receives the full original user prompt, and the tool also saves raw model responses to disk, amplifying exposure and retention of sensitive content. This increases the chance that confidential input is echoed by the model, duplicated across artifacts, and later disclosed through logs, generated files, or workspace sharing.

Ssd 3

Medium
Confidence
98% confidence
Finding
Embedding the original user prompt into SWARM_SUMMARY.md and README.md can expose sensitive request details in user-facing and easily shared files. Because these are top-level project artifacts, they are more likely to be committed, copied, or published than internal logs.

Missing User Warnings

Low
Confidence
82% confidence
Finding
The code loads and uses OPENROUTER_API_KEY from a workspace .env file to authenticate outbound requests. Although this is functionally necessary, the script provides no user-facing disclosure that it accesses local credentials, only error messages if the key is missing.

Static analysis

Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
orchestrator.js:32

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
orchestrator.js:29