Back to skill

Security audit

Web Builder

Security checks for vulnerabilities and agentic risk

Overview

This scaffolding skill is mostly transparent, but its script can overwrite arbitrary writable paths and the generated authenticated API has serious authorization gaps.

Review before installing or using this skill. Use only a safe new project name under an intended directory, avoid existing targets, and treat the generated backend as a prototype until path validation, overwrite protection, object-level authorization, and update field whitelisting are fixed.

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
scripts/scaffold_neo_app.sh:8
Finding
Unvalidated Scaffold Paths Permit File Creation and Overwrite Outside the Intended Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/scaffold_neo_app.sh`, lines 8-29 **Vulnerability Type**: Path traversal and unsafe file overwrite **Risk Level**: High ### Vulnerable Code ```bash while [[ $# -gt 0 ]]; do case "$1" in --name) NAME="$2"; shift 2;; --path) BASE_PATH="$2"; shift 2;; --with-auth) AUTH_MODE="$2"; shift 2;; *) echo "Unknown arg: $1"; exit 1;; esac done if [[ -z "$NAME" ]]; then echo "Usage: scaffold_neo_app.sh --name <project-name> [--path apps] [--with-auth jwt]" exit 1 fi ROOT="$BASE_PATH/$NAME" FRONTEND="$ROOT/frontend" BACKEND="$ROOT/backend" mkdir -p "$FRONTEND/src" "$FRONTEND/public" mkdir -p "$BACKEND/src" "$BACKEND/src/config" "$BACKEND/src/models" "$BACKEND/src/controllers" "$BACKEND/src/routes" "$BACKEND/src/middlewares" cat > "$ROOT/README.md" <<EOF ``` The same unvalidated paths are subsequently used by numerous `cat >` redirections throughout the script, which overwrite existing destination files. ### Technical Analysis The script accepts `BASE_PATH` and `NAME` without validating their contents or canonicalizing the resulting path. A project name can contain `..` path components, while `--path` can directly designate any writable directory. The computed `ROOT` is never checked to ensure that it remains under the intended scaffold directory. Although variable expansion is quoted and therefore does not directly permit shell command injection, quoting does not prevent directory traversal. Shell output redirection also follows symbolic links and truncates an existing destination file before writing to it. The script does not reject an existing project directory, pre-existing files, or symbolic links. This violates the expected filesystem boundary of a scaffolding operation and allows user-controlled arguments to determine where files are created or overwritten. ### Attack Path 1. An attacker or untrusted caller supplies a crafted project path, for example: ```bash bash s ...[truncated 1271 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict project names to a safe slug: ```bash if [[ ! "$NAME" =~ ^[A-Za-z0-9][A-Za-z0-9_-]*$ ]]; then echo "Invalid project name" >&2 exit 1 fi ``` 2. Resolve `BASE_PATH` and `ROOT` to canonical absolute paths, then verify that `ROOT` is a strict descendant of the approved base directory. 3. If arbitrary `--path` values are not required, remove that option and use a fixed trusted output directory. 4. Reject `NAME` values containing `/`, `\`, `.` path segments, control characters, or traversal components. 5. Refuse to operate when the target project directory already exists rather than silently overwriting its contents. 6. Detect and reject symbolic links in every destination path component. 7. Use safe option termination, such as `mkdir -p -- "$path"`, for filesystem commands. 8. Create files exclusively where possible, for example by enabling `noclobber` or using an atomic exclusive-creation mechanism. 9. Add tests covering absolute paths, `../` traversal, existing destinations, and symbolic-link destinations. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/scaffold_neo_app.sh:290
Finding
Generated Item API Lacks Object-Level Authorization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/scaffold_neo_app.sh`, lines 290-322; route exposure at lines 344-348 **Vulnerability Type**: Broken object-level authorization and unsafe mass assignment **Risk Level**: High ### Vulnerable Code ```javascript async function listItems(_req, res, next) { try { const docs = await Item.find().sort({ createdAt: -1 }); res.json(docs); } catch (e) { next(e); } } async function getItem(req, res, next) { try { const doc = await Item.findById(req.params.id); if (!doc) return next({ status: 404, message: 'Not found' }); res.json(doc); } catch (e) { next(e); } } async function updateItem(req, res, next) { try { const doc = await Item.findByIdAndUpdate(req.params.id, req.body, { new: true }); if (!doc) return next({ status: 404, message: 'Not found' }); res.json(doc); } catch (e) { next(e); } } async function deleteItem(req, res, next) { try { const doc = await Item.findByIdAndDelete(req.params.id); if (!doc) return next({ status: 404, message: 'Not found' }); res.json({ ok: true }); } catch (e) { next(e); } } module.exports = { createItem, listItems, getItem, updateItem, deleteItem }; ``` The generated routes expose these operations to every authenticated user: ```javascript router.use(auth); router.post('/', createItem); router.get('/', listItems); router.get('/:id', getItem); router.put('/:id', updateItem); router.delete('/:id', deleteItem); ``` ### Technical Analysis The authentication middleware verifies that a request has a valid JWT, but the generated controller does not enforce ownership or role-based authorization. Item records include a `createdBy` field, yet list, retrieval, update, and deletion queries do not constrain that field to `req.user.sub`. As a result: - `Item.find()` returns records belonging to every user. - `Item.findById()` exposes any record whose identifier is known. - `Item.findByIdAndUpdate()` modifies any record ...[truncated 2031 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Scope ordinary-user queries to the authenticated owner: ```javascript const filter = { _id: req.params.id, createdBy: req.user.sub }; ``` 2. Scope list operations similarly: ```javascript const docs = await Item.find({ createdBy: req.user.sub }) .sort({ createdAt: -1 }); ``` 3. Use ownership-aware update and deletion operations: ```javascript const doc = await Item.findOneAndUpdate( { _id: req.params.id, createdBy: req.user.sub }, allowedUpdates, { new: true, runValidators: true } ); ``` 4. Define an explicit administrator policy if administrators require global access. Check the verified JWT role server-side rather than trusting request-body or query parameters. 5. Whitelist writable fields instead of passing `req.body` directly: ```javascript const { name, description, price } = req.body; const allowedUpdates = { name, description, price }; ``` 6. Never permit ordinary users to update `createdBy`, identifiers, timestamps, roles, or other authorization-sensitive fields. 7. Enable update validation with `runValidators: true` and add schema-level validation for expected field ranges and formats. 8. Return a consistent `404` or `403` response for inaccessible objects according to the application's information-disclosure policy. 9. Add automated authorization tests proving that one user cannot list, read, update, or delete another user's records. 10. Add separate tests for administrator access and mass-assignment attempts. ]]>
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (3)

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill instructs the agent to run a shell script but does not declare any explicit tool scope or permission boundaries. That creates an authorization gap where the skill can trigger code execution or environment access without clear least-privilege controls, increasing the chance of unintended command execution in contexts where the skill is auto-invoked.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The description includes broad activation language such as 'asks to build/create/scaffold a new app,' which could cause the skill to activate for many general software requests beyond the user's intended scope. In a skill that runs scaffolding commands, overbroad triggering increases the risk of unexpected file creation or command execution from ambiguous prompts.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrase "just build it" is broad, informal language that can easily appear in normal conversation, causing this skill to activate when the user did not explicitly intend Neo App Mode. In an agentic coding context, unintended activation can lead to unwanted project scaffolding, stack assumptions, or application generation actions based on incomplete requirements.

Static analysis

No suspicious patterns detected.