Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .github/workflows/kcode-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,12 @@ jobs:
with:
bun-version: latest

- name: Install KCode
run: |
curl -fsSL https://github.com/AstrolexisAI/KCode/releases/latest/download/kcode-linux-x64 -o kcode
chmod +x kcode
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run Audit (static-only)
run: |
./kcode audit ${{ github.event.inputs.path || '.' }} \
bun run src/index.ts audit ${{ github.event.inputs.path || '.' }} \
--skip-verify \
--json \
--max-files ${{ github.event.inputs.max_files || '500' }}
Expand Down
133 changes: 27 additions & 106 deletions AUDIT_REPORT.json
Original file line number Diff line number Diff line change
@@ -1,131 +1,52 @@
{
"project": "/home/curly/KCode",
"timestamp": "2026-04-06",
"timestamp": "2026-04-10",
"languages_detected": [
"typescript",
"swift",
"python",
"lua",
"javascript",
"kotlin",
"ruby"
],
"files_scanned": 500,
"candidates_found": 72,
"confirmed_findings": 7,
"false_positives": 65,
"candidates_found": 110,
"confirmed_findings": 2,
"false_positives": 86,
"findings": [
{
"pattern_id": "react-001-dangerously-set",
"pattern_title": "dangerouslySetInnerHTML with dynamic content",
"severity": "high",
"file": "/home/curly/KCode/benchmarks/certification/tasks.ts",
"line": 930,
"matched_text": "dangerouslySetInnerHTML={{ __html: ",
"context": "928: \\`\\`\\`tsx\n929: function UserComment({ comment }: { comment: string }) {\n930: return <div dangerouslySetInnerHTML={{ __html: comment }} />;\n931: }\n932: \\`\\`\\`\n933: ",
"verification": {
"verdict": "confirmed",
"reasoning": "The `__html` property in `dangerouslySetInnerHTML` is assigned directly from the `comment` prop, which originates from user input stored in the database—making it dynamic and vulnerable to XSS.",
"execution_path": "`UserComment` component receives `comment` (user-controlled string) → passed as `__html` value to `dangerouslySetInnerHTML` → React injects raw HTML into DOM without escaping → malicious script tags or event handlers in `comment` execute in context of the app.",
"suggested_fix": "Wrap `comment` with a sanitizer like DOMPurify before assignment: `<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(comment) }} />` or at minimum escape HTML entities if DOMPurify is unavailable."
},
"cwe": "CWE-79"
},
{
"pattern_id": "js-002-innerhtml",
"pattern_title": "innerHTML/outerHTML with dynamic content (XSS)",
"severity": "high",
"file": "/home/curly/KCode/ide/vscode/src/sidebar.ts",
"line": 322,
"matched_text": ".innerHTML = ",
"context": "320: div.className = 'message ' + role;\n321: if (id) div.dataset.id = id;\n322: div.innerHTML = '<div class=\"role\">' + (role === 'user' ? 'You' : 'KCode') + '</div>' +\n323: '<div class=\"content\">' + escapeHtml(content) + '</div>';\n324: messagesEl.appendChild(div);\n325: messagesEl.scrollTop = messagesEl.scrollHeight;",
"pattern_id": "js-014-json-parse-no-catch",
"pattern_title": "JSON.parse without try/catch (crash on invalid input)",
"severity": "medium",
"file": "/home/curly/KCode/src/tools/notebook-utils.ts",
"line": 36,
"matched_text": "JSON.parse(content",
"context": "34: /** Parse .ipynb JSON content into a typed notebook structure */\n35: export function parseNotebook(content: string): JupyterNotebook {\n36: const nb = JSON.parse(content);\n37: if (nb.nbformat !== 4) {\n38: throw new Error(`Only nbformat 4 is supported, found: ${nb.nbformat}`);\n39: }",
"verification": {
"verdict": "confirmed",
"reasoning": "The `content` parameter passed to `addMessage()` is escaped via `escapeHtml()` before being injected into `innerHTML`, but the function itself is called with dynamic content (e.g., from LLM responses or user input), and `escapeHtml()` only protects against basic HTML injection—not all XSS vectors like event handlers or script tags if the escaping is incomplete or bypassed. (+1 more matches of this pattern in the same file)",
"execution_path": "`addMessage(role, content, id)` → `content` (dynamic, e.g., from API/LLM/user) → `escapeHtml(content)` → embedded in `div.innerHTML` string → rendered as HTML",
"suggested_fix": "Replace `div.innerHTML = ...` with `div.innerHTML = '<div class=\"role\">...</div><div class=\"content\">' + escapeHtml(content) + '</div>';` → `div.innerHTML = new DOMParser().parseFromString('<div class=\"role\">...</div><div class=\"content\">' + escapeHtml(content) + '</div>', 'text/html').documentElement.innerHTML;` OR (more simply) use `div.insertAdjacentHTML('beforeend', ...)` with sanitized input, or replace `innerHTML` with `textContent` if full HTML isn’t needed."
"reasoning": "The `JSON.parse(content)` call is not wrapped in a try/catch block, meaning any malformed JSON input will throw an unhandled SyntaxError.",
"execution_path": "parseNotebook(invalidJsonString) -> JSON.parse(invalidJsonString) -> throws SyntaxError",
"suggested_fix": "Wrap the JSON.parse call in a try/catch block and throw a more descriptive error or return a result type."
},
"cwe": "CWE-79"
"cwe": "CWE-754"
},
{
"pattern_id": "js-002-innerhtml",
"pattern_title": "innerHTML/outerHTML with dynamic content (XSS)",
"pattern_id": "js-008-prototype-pollution-bracket",
"pattern_title": "Prototype pollution via bracket notation with user key",
"severity": "high",
"file": "/home/curly/KCode/vscode-extension/src/chat-panel.ts",
"line": 507,
"matched_text": ".innerHTML = ",
"context": "505: div.className = 'message message-' + role;\n506: if (role === 'assistant') {\n507: div.innerHTML = formatMarkdown(content);\n508: } else {\n509: div.textContent = content;\n510: }",
"verification": {
"verdict": "confirmed",
"reasoning": "The `content` parameter passed to `addMessage()` is used directly in `formatMarkdown(content)` and assigned to `div.innerHTML`, and since `content` originates from external sources (e.g., user messages, LLM responses), it likely contains untrusted HTML that can execute scripts. (+3 more matches of this pattern in the same file)",
"execution_path": "`addMessage(role, content)` → `formatMarkdown(content)` returns HTML string → `div.innerHTML = ...` → appended to `messagesEl`",
"suggested_fix": "Replace `div.innerHTML = formatMarkdown(content);` with `div.innerHTML = sanitizeHtml(formatMarkdown(content));` using a trusted sanitizer (e.g., DOMPurify) or use `textContent` if Markdown rendering is not strictly needed for untrusted input."
},
"cwe": "CWE-79"
},
{
"pattern_id": "js-002-innerhtml",
"pattern_title": "innerHTML/outerHTML with dynamic content (XSS)",
"severity": "high",
"file": "/home/curly/KCode/src/web/static/app.js",
"line": 357,
"matched_text": ".innerHTML = ",
"context": "355: var rendered = window.MarkdownRenderer.renderMarkdown(msg.content);\n356: if (window.DOMPurify) {\n357: body.innerHTML = window.DOMPurify.sanitize(rendered);\n358: } else {\n359: body.innerHTML = rendered;\n360: }",
"verification": {
"verdict": "confirmed",
"reasoning": "The `rendered` value assigned to `body.innerHTML` originates from `window.MarkdownRenderer.renderMarkdown(msg.content)`, and `msg.content` is user-provided (as seen from `msg.id`, `msg.role`, etc.), making it susceptible to XSS unless sanitized—here, DOMPurify is used conditionally, but fallback to unsanitized HTML exists. (+2 more matches of this pattern in the same file)",
"execution_path": "`renderMarkdown(msg.content)` → `rendered` (HTML string with potential XSS payloads) → `body.innerHTML = rendered` (or `DOMPurify.sanitize(rendered)`) → DOM injection → script execution on render",
"suggested_fix": "Ensure `window.DOMPurify` is always available or polyfilled before use; if not, add a fallback sanitizer (e.g., `DOMPurify.sanitize || ((html) => html)`) or use `textContent` for high-risk contexts."
},
"cwe": "CWE-79"
},
{
"pattern_id": "js-006-hardcoded-secret",
"pattern_title": "Hardcoded secret/key in JavaScript/TypeScript",
"severity": "high",
"file": "/home/curly/KCode/src/remote/triggers/trigger-api.test.ts",
"line": 9,
"matched_text": "TOKEN = \"test-token-abc123\"",
"context": "7: \n8: const BASE_URL = \"https://cloud.kulvex.ai/api/v1\";\n9: const AUTH_TOKEN = \"test-token-abc123\";\n10: \n11: const sampleTrigger: RemoteTrigger = {\n12: id: \"trg_001\",",
"verification": {
"verdict": "confirmed",
"reasoning": "The `AUTH_TOKEN` is a hardcoded string `\"test-token-abc123\"` that follows a realistic pattern (alphanumeric with hyphen) and is used directly in authentication, suggesting it is not merely a placeholder but a representative value likely copied from actual usage or configuration.",
"execution_path": "Test file imports `TriggerApiClient`, which presumably uses `AUTH_TOKEN` (defined at module scope) when making authenticated requests to `BASE_URL`; this token is exposed at module load time and visible in any test run or bundle.",
"suggested_fix": "Replace `AUTH_TOKEN` with an environment variable, e.g., `process.env.TRIGGER_API_TOKEN ?? \"test-token-abc123\"`, and ensure it is set in CI/test environments."
},
"cwe": "CWE-798"
},
{
"pattern_id": "js-007-command-injection",
"pattern_title": "Shell command with template literal (injection)",
"severity": "critical",
"file": "/home/curly/KCode/src/ui/actions/text-actions.ts",
"line": 157,
"matched_text": "execSync(`",
"context": "155: try {\n156: const bin = cmd.split(\" \")[0]!;\n157: execSync(`which ${bin} 2>/dev/null`, { timeout: 2000 });\n158: const output = execSync(`${cmd} '${text.replace(/'/g, \"'\\\\''\")}' 2>/dev/null`, {\n159: timeout: 5000,\n160: }).toString();",
"verification": {
"verdict": "confirmed",
"reasoning": "The template literal on line 157 (`which ${bin}`) and especially line 158 (`${cmd} '${text.replace(/'/g, \"'\\\\''\")}'`) incorporates external input: `bin` is derived from the command string (`cmd`) in the loop, and `text` comes from `args.trim().slice(0, 20)`—which originates from user-provided input via the `/ascii <text>` command. (+1 more matches of this pattern in the same file)",
"execution_path": "User invokes `/ascii <malicious-text>` → `args.trim()` extracts user input → `text` is set → `execSync` runs shell commands using template literals that interpolate `bin` and `text` → shell interprets injected characters (e.g., `;`, `|`, `$()`) if present in `text`.",
"suggested_fix": "Replace template literals with array-based command invocation (e.g., `execSync(['which', bin], ...)` and `execSync([...cmd.split(' '), `'${text.replace(/'/g, \"'\\\\''\")}'`], ...)`), or use `spawn` with explicit argv to avoid shell interpretation of user-controlled parts."
},
"cwe": "CWE-78"
},
{
"pattern_id": "js-007-command-injection",
"pattern_title": "Shell command with template literal (injection)",
"severity": "critical",
"file": "/home/curly/KCode/src/tools/diff-viewer.ts",
"line": 171,
"matched_text": "execSync(",
"context": "169: const safeFile = resolvedFile.replace(/[\"`$\\\\]/g, \"\");\n170: const safeCtx = String(Math.min(Math.max(parseInt(String(contextLines)) || 3, 0), 100));\n171: const result = execSync(\n172: `git diff ${flag} -U${safeCtx} -- \"${safeFile}\" 2>&1 || true`,\n173: {\n174: cwd: process.cwd(),",
"file": "/home/curly/KCode/src/core/mcp.ts",
"line": 167,
"matched_text": "validated[name] =",
"context": "165: for (const [name, config] of Object.entries(configs)) {\n166: if (isValidServerConfig(config)) {\n167: validated[name] = config as McpServerConfig;\n168: }\n169: }\n170: if (Object.keys(validated).length === 0) return;",
"verification": {
"verdict": "confirmed",
"reasoning": "The template literal on line 172 includes `flag`, `safeCtx`, and `safeFile`, where `safeFile` is derived from `resolvedFile` (likely user-controlled via `file`) and `safeCtx` from `contextLines`, both of which may originate from external input (e.g., user-provided file paths or context line counts), enabling shell injection despite partial sanitization.",
"execution_path": "`execSync` → `git diff ${flag} -U${safeCtx} -- \"${safeFile}\" 2>&1 || true` triggered when `msg.includes(\"Command failed\") && msg.includes(\"git diff\")` in the catch block, which itself follows an initial `git diff` failure.",
"suggested_fix": "Escape or quote all interpolated values robustly: replace `resolvedFile.replace(/[\"`$\\\\]/g, \"\")` with a more comprehensive sanitizer (e.g., `safeFile = `\"${resolvedFile.replace(/'/g, \"'\\\"'\\\"'\")}\"`) and ensure `flag` and `contextLines` are validated against whitelists or numeric ranges before interpolation."
"reasoning": "The `name` key is taken directly from the `configs` object (sourced from a CLI config file) and used to assign a value to the `validated` object without filtering for sensitive keys like `__proto__`, `constructor`, or `prototype`. (+1 more matches of this pattern in the same file)",
"execution_path": "User provides a config file containing a `__proto__` key -> `loadFromConfigs` is called with the parsed JSON -> `Object.entries(configs)` yields the `__proto__` key -> `validated[name] = config` pollutes the global Object prototype.",
"suggested_fix": "Add a check to skip forbidden keys: `if (name === '__proto__' || name === 'constructor' || name === 'prototype') continue;`"
},
"cwe": "CWE-78"
"cwe": "CWE-1321"
}
],
"elapsed_ms": 87571
"elapsed_ms": 1602418
}
Loading
Loading