T09 · Insecure Skill Coding Practices
- Location
SKILL.md:167- Finding
Parent-directory symlink allows secrets to be written outside the intended storage directory
- Content
View full analysis
{ const res = await fetch("http://127.0.0.1:7331/secret", { method: "POST", headers: { authorization: `Bearer ${key}`, "content-type": "application/json" }, body: JSON.stringify({ project, keys: allow }), }); if (!res.ok) { console.error(`abra /secret failed: HTTP ${res.status}`); process.exit(1); } const j = await res.json(); if (j.error) { console.error("abra error (see abra serve log)"); process.exit(1); } const out = {}; for (const k of allow) if (typeof j[k] === "string") out[k] = j[k]; // opaque fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); if (fs.lstatSync(dir).uid !== os.userInfo().uid) { console.error("agent-env dir not owned by user"); process.exit(1); } try { if (fs.lstatSync(file).isSymbolicLink()) { console.error("refusing symlink"); process.exit(1); } } catch {} const fd = fs.openSync(file, "wx", 0o600); // exclusive: fails if it already exists ``` ### Technical Analysis The example intends to constrain secret files to `~/.abracadabra/agent-env`, but the containment check uses `path.resolve()`, which performs lexical path normalization and does not resolve filesystem symlinks. The code verifies that `dir` is owned by the current user but does not verify that it is a real directory rather than a symbolic link: ```js fs.lstatSync(dir).uid !== os.userInfo().uid ``` For a user-owned symbolic link, `lstatSync(dir).uid` ordinarily reports the owner of the link itself. Consequently, a link such as: ```text ~/.abracadabra/agent-env -> /another/user-owned ...[truncated 2287 chars]- Remediation
View remediation
