T09 · Insecure Skill Coding Practices
- Location
scripts/security-check.js:128- Finding
Sensitive Values Exposed in Sanitization Results
- Content
View full analysis
Vulnerability Details
File Location:
scripts/security-check.js, lines 128–145
Vulnerability Type: Plaintext sensitive-data exposure
Risk Level: Mediumjs for (const { pattern, replacement } of SENSITIVE_PATTERNS) { const matches = sanitized.match(pattern) if (matches) { for (const match of matches) { removed.push({ original: match, pattern: pattern.toString() }) } sanitized = sanitized.replace(pattern, replacement) } } return { original_length: command.length, sanitized, removed_patterns: removed.map(r => r.original), }Technical Analysis
The sanitization function correctly replaces recognized sensitive values in the
sanitizedstring, but it also stores every original match in theremovedarray. The returnedremoved_patternsproperty then exposes those original values verbatim.This defeats the purpose of sanitization because the resulting object still contains the API keys, bearer tokens, passwords, internal network addresses, or user paths that were supposed to be removed. When invoked through the command-line interface, the complete object is serialized to JSON and printed to standard output, where it may be captured by terminal history, application logs, CI/CD logs, chat records, or monitoring systems.
Attack Path
- A user or calling system passes a command containing a supported sensitive value to
sanitizeCommand. - A configured regular expression identifies the sensitive substring.
- The function stores the unredacted match in
removed. - The command text is redacted, creating the appearance of successful sanitization.
- The function copies the original value into
removed_patterns. - The caller records, shares, or logs the returned object under the assumption that it contains no sensitive data.
- Anyone with access to that output can recover the original matched credential or other sensitive value.
Impa
...[truncated 696 chars]
- A user or calling system passes a command containing a supported sensitive value to
- Remediation
View remediation
Remediation Suggestions
-
Never retain or return the original matched values. Record only non-sensitive metadata, such as a stable pattern identifier and the number of replacements.
-
Replace
removed_patterns: removed.map(r => r.original)with a structure that cannot reveal input content, for example:js const removed = [] for (const { pattern, replacement, id = 'sensitive_value' } of SENSITIVE_PATTERNS) { const matches = sanitized.match(pattern) if (matches) { removed.push({ pattern: id, count: matches.length, }) sanitized = sanitized.replace(pattern, replacement) } } return { original_length: command.length, sanitized, removed_patterns: removed, } -
Assign explicit, non-sensitive identifiers to each configured pattern, such as
api_key,bearer_token, orpassword. -
Minimize the lifetime of raw command data and avoid copying sensitive substrings into intermediate arrays.
-
Add automated tests that insert representative secrets and assert that none of them appears anywhere in the serialized return object.
-
Document that callers must avoid logging raw function inputs and should restrict access to existing logs that may contain prior unredacted results.
-
Review and rotate any real credentials that may already have been processed and subsequently stored in output logs.
-
