T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/calibredb_read.mjs:306
- Finding
- Calibre Password Exposed in Process Arguments and Error Messages<![CDATA[ ## Vulnerability Details **File Location**: `scripts/calibredb_read.mjs:306-314`; `scripts/run_analysis_pipeline.py:8-12, 191-202` **Vulnerability Type**: Plaintext credential exposure **Risk Level**: High ### Vulnerable Code ```javascript function commonArgs(args) { const r = ['--with-library', String(args['with-library'] || '')]; const auth = args.__resolved_auth || resolveAuth(args); if (auth.username) r.push('--username', auth.username); if (auth.password) r.push('--password', auth.password); return r; } function run(cmd) { const cp = spawnSync(cmd[0], cmd.slice(1), { encoding: 'utf8' }); if (cp.status !== 0) { throw new Error(`calibredb failed (${cp.status})\nCMD: ${cmd.map(x => JSON.stringify(x)).join(' ')}\nERR:\n${(cp.stderr || '').trim()}`); } return cp.stdout || ''; } ``` The Python pipeline has the same issue: ```python def run(cmd: list[str]) -> str: cp = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) if cp.returncode != 0: raise RuntimeError(f"cmd failed ({cp.returncode}): {' '.join(cmd)}\n{cp.stderr}") return cp.stdout ``` ```python pw = os.environ.get(ns.password_env, "") auth = [] if ns.username: auth += ["--username", ns.username] if pw: auth += ["--password", pw] rows = json.loads(run([ "calibredb", "--with-library", ns.with_library, *auth, "list", "--for-machine", "--search", f"id:{ns.book_id}", "--fields", "id,title,tags,formats", "--limit", "2" ])) ``` ### Technical Analysis The Calibre password is copied from an environment variable into the child process argument vector as the value following `--password`. Command-line arguments may be visible to other local processes through operating-system process inspection facilities. More critically, both implementations include the complete command in failure messages. Any failed `calibredb` operation therefore causes the plaintext password to be copied into an exception. Tha ...[truncated 1385 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Do not include a password-bearing command in exceptions or logs. 2. Add a central redaction function that replaces the value after `--password` with `[REDACTED]` before formatting diagnostics. 3. Use a protected credential mechanism supported by Calibre instead of command-line arguments where possible. 4. If command-line authentication cannot be avoided, isolate the process and ensure process inspection is restricted. 5. Return only the executable name, exit status, and sanitized stderr in failures. 6. Apply the same redaction controls to both JavaScript and Python implementations. 7. Add automated tests that deliberately fail a command and assert that a known test password is absent from stdout, stderr, exceptions, and state files. 8. Rotate any password that may already have appeared in execution logs. ]]>
