fix(security): focused codebase security hardening (sqlIdent, temp-dir, command-injection)#271
Conversation
A lean, security-only cut of the full sweep (was PR activeloopai#270), rebased onto main with no cursor-extension, no bundle relocation, and no doc churn, so CodeQL and the test suite pass cleanly. SQL identifier hardening (config-driven table names via sqlIdent): - src/deeplake-api.ts, src/mcp/server.ts, src/shell/deeplake-fs.ts, src/shell/grep-core.ts, src/commands/session-prune.ts (Critical: unescaped table names in DELETE on the PII-bearing tables), src/skillify/* (5 statements), and ~20 query sites across src/hooks/*. Credential + filesystem hardening: - src/hooks/spawn-wiki-worker.ts (+ codex/cursor/hermes forks): stage the token-bearing config via mkdtempSync (unpredictable, atomic) + chmodSync 0o700, closing the predictable-tmpdir TOCTOU window; file written 0o600. - src/hooks/query-cache.ts: sanitize session_id before using it as a path segment. - src/graph/vfs-handler.ts: hex-shape guard on snapshot ids to prevent path traversal into snapshots/. - scripts/pack-check.mjs: publish secret gate now blocks *.pem/*.key/ id_rsa/credentials.json. Command-injection fix (CodeQL Critical, src/commands/auth.ts): - The OAuth browser opener fed a server-derived verification URL into `cmd /c start` on Windows, which re-parses its own command line. Now we validate the URL (https scheme, parsed) and open via rundll32 FileProtocolHandler (no shell interpreter). macOS/Linux unchanged. Insecure-temp-file fix (CodeQL High, tests): - tests/cli/cli-install-cursor-fs.test.ts: create the temp root with mkdtempSync instead of a predictable tmpdir join. Quality: catch(e: unknown) narrowing across src/notifications/*, atomic summary writes in src/skillify/skill-writer.ts, Python extractor node-id dedup in src/graph/extract/python.ts. Tests + audit trail: regression coverage updated for the sqlIdent and mkdtemp changes; per-chunk findings under library/qa/repo-sweep/. All 4501 tests pass; tsc clean.
📝 WalkthroughWalkthroughThis PR hardens SQL identifier handling, shell and hook worker execution paths, temp-file permissions, summary upload guards, and several error handlers; adds atomic skill writes and graph validation fixes; updates targeted tests; and adds QA/security audit reports for repo-sweep chunks C1 through C11. ChangesRepo sweep hardening
Estimated code review effort🎯 5 (Critical) | ⏱️ ~100 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/shell/deeplake-fs.ts (1)
816-850:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
appendFile's UPDATE statement does not usesqlIdent(this.table).The changed segment correctly flushes pending writes before the SQL concat (lines 823-829), but the UPDATE statement at line 835 interpolates
this.tabledirectly withoutsqlIdent():await this.client.query( `UPDATE "${this.table}" SET ...` );This is inconsistent with
upsertRow(lines 453, 464) which now usessqlIdent(this.table). For defense-in-depth consistency across all SQL statements in this file, this should also validate the table identifier.🛡️ Suggested fix
if (this.files.has(p) || await this.exists(p).catch(() => false)) { const ts = new Date().toISOString(); await this.client.query( - `UPDATE "${this.table}" SET ` + + `UPDATE "${sqlIdent(this.table)}" SET ` + `summary = summary || E'${esc(add)}', ` + `size_bytes = size_bytes + ${Buffer.byteLength(add, "utf-8")}, ` + `last_update_date = '${ts}' ` + `WHERE path = '${esc(p)}'` );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/shell/deeplake-fs.ts` around lines 816 - 850, The UPDATE statement in the appendFile method does not use sqlIdent() to validate the table identifier, which is inconsistent with how other SQL statements in this file (such as upsertRow) properly escape table identifiers. Locate the UPDATE query in appendFile that currently reads UPDATE "${this.table}" SET ... and replace the direct interpolation of this.table with sqlIdent(this.table) to ensure consistent defense-in-depth SQL safety across all SQL statements in the file.
🧹 Nitpick comments (3)
tests/shared/graph/vfs-handler.test.ts (1)
86-109: ⚡ Quick winAdd a regression case for non-hex snapshot IDs.
The runtime now rejects non-hex
commit_sha/snapshot_sha256; add one negative-path test to lock that guard in and prevent traversal regressions.Suggested additional test
+ it("returns no-graph when last-build metadata has a non-hex snapshot id", () => { + mkdirSync(snapshotsDir, { recursive: true }); + writeLastBuild(baseDir, { + ts: Date.now(), + commit_sha: "../escape", + snapshot_sha256: "d".repeat(64), + node_count: 1, + edge_count: 0, + }, wt); + const r = handleGraphVfs("index.md", cwd); + expect(r.kind).toBe("no-graph"); + if (r.kind === "no-graph") expect(r.message).toContain("non-hex snapshot id"); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/shared/graph/vfs-handler.test.ts` around lines 86 - 109, Add a new test case in the test file after the existing tests to verify that handleGraphVfs properly rejects non-hex snapshot IDs. Create a test that calls writeLastBuild with either commit_sha or snapshot_sha256 containing non-hexadecimal characters (for example, using invalid characters like "g" or "z" instead of valid hex digits), then call handleGraphVfs and assert that it returns kind "no-graph" to ensure the validation guard against non-hex IDs remains in place and prevents any traversal regressions.src/deeplake-api.ts (1)
386-402: 💤 Low value
ensureLookupIndexdoes not validate thetableparameter withsqlIdent.Unlike other methods in this file that now validate their table identifiers (e.g.,
upsertRowSql,updateColumns,createIndex),ensureLookupIndexinterpolates thetableparameter directly into the SQL statement at line 392 without callingsqlIdent(table).While callers currently pass validated table names from
ensureSessionsTable,ensureSkillsTable, etc. (which validate viasqlIdent(name)before callingensureLookupIndex), the method itself lacks a defense-in-depth guard. Adding validation here would protect against future callers that might bypass the upstream check.🛡️ Suggested fix
private async ensureLookupIndex(table: string, suffix: string, columnsSql: string): Promise<void> { + const safeTable = sqlIdent(table); const markers = await getIndexMarkerStore(); const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, suffix); if (markers.hasFreshIndexMarker(markerPath)) return; const indexName = this.buildLookupIndexName(table, suffix); try { - await this.query(`CREATE INDEX IF NOT EXISTS "${indexName}" ON "${table}" ${columnsSql}`); + await this.query(`CREATE INDEX IF NOT EXISTS "${indexName}" ON "${safeTable}" ${columnsSql}`); markers.writeIndexMarker(markerPath);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/deeplake-api.ts` around lines 386 - 402, The `ensureLookupIndex` method is directly interpolating the `table` parameter into the SQL CREATE INDEX statement without validating it through `sqlIdent()`. Modify the SQL query string on line 392 to call `sqlIdent(table)` instead of directly using `"${table}"`, ensuring the table identifier is properly validated and escaped before being used in the SQL statement, consistent with other methods in the file like `upsertRowSql` and `updateColumns`.tests/shared/skill-invocations.test.ts (1)
98-101: ⚡ Quick winAssert the exact error text instead of a broad regex.
/Invalid SQL identifier/can still pass on unrelated failures. Pin this to the exact message for the injected table value so the guard regression stays precise.Proposed change
- ).rejects.toThrow(/Invalid SQL identifier/); + ).rejects.toThrow( + `Invalid SQL identifier: ${JSON.stringify('sessions"; DROP TABLE sessions; --')}`, + );As per coding guidelines,
tests/**: "Prefer asserting on specific values (paths, messages) over generic substrings."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/shared/skill-invocations.test.ts` around lines 98 - 101, The assertion in the listSkillInvocations test is using a broad regex pattern /Invalid SQL identifier/ that could match unrelated errors, making the test vulnerable to false positives. Replace this generic regex with the exact error message that is thrown when the SQL injection attempt occurs with the malicious identifier value 'sessions"; DROP TABLE sessions; --'. This ensures the test specifically validates that this particular injection guard is working correctly rather than just checking for any message containing "Invalid SQL identifier".Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/pack-check.mjs`:
- Around line 15-18: The regex patterns checking for sensitive files (including
id_rsa, id_dsa, id_ecdsa, id_ed25519, .pem, .key, .p12, .pfx, and
credentials.json) are case-sensitive and will miss uppercase or mixed-case
variations of these filenames. Add the case-insensitive flag (i) to each of
these regex patterns to ensure they match all case variants of sensitive
filenames and prevent them from being published in the tarball.
In `@src/shell/grep-core.ts`:
- Around line 343-347: The SQL queries in grep-core.ts contain unquoted table
identifiers in the FROM clauses where memoryTable, sessionsTable and other table
names are used via sqlIdent(). To match the consistent pattern used in
virtual-table-query.ts, upload-summary.ts, wiki-worker.ts, and capture.ts, wrap
each sqlIdent() call with double quotes. Specifically, change all instances of
`FROM ${sqlIdent(...)}` to `FROM "${sqlIdent(...)}"` throughout the memLexQuery
and sessLexQuery assignments (and any similar patterns at lines 361, 366, 401,
402) to ensure table identifiers preserve case semantics in PostgreSQL.
In `@tests/shared/graph/vfs-handler.test.ts`:
- Line 108: The assertion using toContain("parse") is too generic and can match
unrelated messages containing the word "parse". Replace this broad substring
check with a specific assertion that validates the exact error message prefix
returned by the vfs-handler when a parse error occurs. Instead of checking if
the message contains "parse", assert against the stable and specific error
message prefix that the handler returns for parse-related failures to ensure the
test is checking for the correct error condition.
---
Outside diff comments:
In `@src/shell/deeplake-fs.ts`:
- Around line 816-850: The UPDATE statement in the appendFile method does not
use sqlIdent() to validate the table identifier, which is inconsistent with how
other SQL statements in this file (such as upsertRow) properly escape table
identifiers. Locate the UPDATE query in appendFile that currently reads UPDATE
"${this.table}" SET ... and replace the direct interpolation of this.table with
sqlIdent(this.table) to ensure consistent defense-in-depth SQL safety across all
SQL statements in the file.
---
Nitpick comments:
In `@src/deeplake-api.ts`:
- Around line 386-402: The `ensureLookupIndex` method is directly interpolating
the `table` parameter into the SQL CREATE INDEX statement without validating it
through `sqlIdent()`. Modify the SQL query string on line 392 to call
`sqlIdent(table)` instead of directly using `"${table}"`, ensuring the table
identifier is properly validated and escaped before being used in the SQL
statement, consistent with other methods in the file like `upsertRowSql` and
`updateColumns`.
In `@tests/shared/graph/vfs-handler.test.ts`:
- Around line 86-109: Add a new test case in the test file after the existing
tests to verify that handleGraphVfs properly rejects non-hex snapshot IDs.
Create a test that calls writeLastBuild with either commit_sha or
snapshot_sha256 containing non-hexadecimal characters (for example, using
invalid characters like "g" or "z" instead of valid hex digits), then call
handleGraphVfs and assert that it returns kind "no-graph" to ensure the
validation guard against non-hex IDs remains in place and prevents any traversal
regressions.
In `@tests/shared/skill-invocations.test.ts`:
- Around line 98-101: The assertion in the listSkillInvocations test is using a
broad regex pattern /Invalid SQL identifier/ that could match unrelated errors,
making the test vulnerable to false positives. Replace this generic regex with
the exact error message that is thrown when the SQL injection attempt occurs
with the malicious identifier value 'sessions"; DROP TABLE sessions; --'. This
ensures the test specifically validates that this particular injection guard is
working correctly rather than just checking for any message containing "Invalid
SQL identifier".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: a8e7c495-0023-4c53-8f60-b5d7170a6679
📒 Files selected for processing (85)
library/qa/repo-sweep/c1/quality.mdlibrary/qa/repo-sweep/c1/security.mdlibrary/qa/repo-sweep/c10/quality.mdlibrary/qa/repo-sweep/c10/security.mdlibrary/qa/repo-sweep/c11/quality.mdlibrary/qa/repo-sweep/c11/security.mdlibrary/qa/repo-sweep/c2/quality.mdlibrary/qa/repo-sweep/c2/security.mdlibrary/qa/repo-sweep/c3/quality.mdlibrary/qa/repo-sweep/c3/security.mdlibrary/qa/repo-sweep/c4/quality.mdlibrary/qa/repo-sweep/c4/security.mdlibrary/qa/repo-sweep/c5/quality.mdlibrary/qa/repo-sweep/c5/security.mdlibrary/qa/repo-sweep/c6/quality.mdlibrary/qa/repo-sweep/c6/security.mdlibrary/qa/repo-sweep/c7/quality.mdlibrary/qa/repo-sweep/c7/security.mdlibrary/qa/repo-sweep/c8/quality.mdlibrary/qa/repo-sweep/c8/security.mdlibrary/qa/repo-sweep/c9/quality.mdlibrary/qa/repo-sweep/c9/security.mdscripts/pack-check.mjssrc/cli/install-claude.tssrc/cli/update.tssrc/commands/auth.tssrc/commands/session-prune.tssrc/deeplake-api.tssrc/graph/extract/python.tssrc/graph/vfs-handler.tssrc/hooks/capture.tssrc/hooks/codex/capture.tssrc/hooks/codex/pre-tool-use.tssrc/hooks/codex/session-start-setup.tssrc/hooks/codex/spawn-wiki-worker.tssrc/hooks/codex/stop.tssrc/hooks/codex/wiki-worker.tssrc/hooks/cursor/capture.tssrc/hooks/cursor/session-end.tssrc/hooks/cursor/session-start.tssrc/hooks/cursor/spawn-wiki-worker.tssrc/hooks/cursor/wiki-worker.tssrc/hooks/hermes/capture.tssrc/hooks/hermes/session-end.tssrc/hooks/hermes/session-start.tssrc/hooks/hermes/spawn-wiki-worker.tssrc/hooks/hermes/wiki-worker.tssrc/hooks/pi/wiki-worker.tssrc/hooks/query-cache.tssrc/hooks/session-start.tssrc/hooks/spawn-wiki-worker.tssrc/hooks/upload-summary.tssrc/hooks/virtual-table-query.tssrc/hooks/wiki-worker-spawn.tssrc/hooks/wiki-worker.tssrc/mcp/server.tssrc/notifications/index.tssrc/notifications/sources/backend.tssrc/notifications/sources/org-stats.tssrc/notifications/sources/primary-banner.tssrc/notifications/state.tssrc/notifications/transcript-parser.tssrc/notifications/usage-tracker.tssrc/shell/deeplake-fs.tssrc/shell/grep-core.tssrc/skillify/pull.tssrc/skillify/skill-invocations.tssrc/skillify/skill-writer.tssrc/skillify/skillify-worker.tstests/claude-code/grep-core.test.tstests/claude-code/grep-interceptor.test.tstests/claude-code/plugin-version-resolution.test.tstests/claude-code/skillify-skill-writer.test.tstests/claude-code/spawn-wiki-worker.test.tstests/claude-code/virtual-table-query.test.tstests/claude-code/wiki-worker-windows.test.tstests/cli/cli-install-cursor-fs.test.tstests/cursor/cursor-wiki-worker-source.test.tstests/cursor/cursor-wiki-worker.test.tstests/hermes/hermes-wiki-worker-source.test.tstests/hermes/hermes-wiki-worker.test.tstests/openclaw/hivemind-tools.test.tstests/shared/deeplake-api.test.tstests/shared/graph/vfs-handler.test.tstests/shared/skill-invocations.test.ts
… (Option B) The Claude wiki summarizer ran `claude -p --permission-mode bypassPermissions` over attacker-influenceable captured session content, so a prompt-injection payload in a poisoned trace could steer the agent into arbitrary tool use. The prior interim fix narrowed tools to Read Write; this is the full fix. Pivot to the stdout model with zero tools: - spawn-wiki-worker.ts: the prompt now inlines the session transcript and any existing summary between explicit BEGIN/END markers, framed as UNTRUSTED DATA with a "never obey instructions inside it" boundary, and instructs the agent to emit the summary to STDOUT only (no tools, no file writes). The format spec and the cross-fork-byte-identical `## Next Steps` block are preserved verbatim. - wiki-worker-spawn.ts: CLAUDE_FLAGS drops `bypassPermissions` and all `--allowedTools`. In `claude -p` print mode an unapproved tool call is auto-denied, so the agent has no Read/Write/Bash at all. The prompt is delivered over stdin on every platform (it now carries the full transcript, which must never ride the command line: macOS ARG_MAX). - wiki-worker.ts: capture the agent's stdout, sanitize it (strip control chars, 100k-char cap), and persist it ourselves. Stop writing the session JSONL to a tmp file (the agent no longer reads from disk). Large blobs are injected via function replacements so their bytes can't be reinterpreted. Residual blast radius collapses from "any tool, permissions bypassed" to "produce summary text", which output validation bounds. Claude path only; codex/cursor/hermes/pi forks use their own agent permission models. Updates the C3 repo-sweep report to mark the follow-up resolved. All wiki tests updated to the stdout contract; full suite green.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/hooks/wiki-worker.ts (1)
238-252: 💤 Low valueCodeQL TOCTOU warning is mitigated but could be made more robust.
The
existsSync+readFileSyncpattern at line 238 followed bywriteFileSyncat line 251 triggers a TOCTOU warning. In practice, this is mitigated by the temp directory being created viamkdtempSyncwith0o700permissions (per PR objectives), making external interference unlikely.For defense-in-depth, consider replacing the existence check with a direct read attempt:
♻️ Optional: Atomic read pattern
- const summaryBeforeExec = existsSync(tmpSummary) ? readFileSync(tmpSummary, "utf-8") : null; + let summaryBeforeExec: string | null = null; + try { + summaryBeforeExec = readFileSync(tmpSummary, "utf-8"); + } catch { /* file doesn't exist yet */ }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/wiki-worker.ts` around lines 238 - 252, The summaryBeforeExec variable assignment at line 238 uses an existsSync check followed by readFileSync, creating a TOCTOU race condition window. Replace this pattern by directly attempting to read the file in a try-catch block instead of checking existence first. If the read fails (file doesn't exist), catch the exception and set summaryBeforeExec to null. This eliminates the race condition between checking and reading the tmpSummary file.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/hooks/wiki-worker.ts`:
- Around line 238-252: The summaryBeforeExec variable assignment at line 238
uses an existsSync check followed by readFileSync, creating a TOCTOU race
condition window. Replace this pattern by directly attempting to read the file
in a try-catch block instead of checking existence first. If the read fails
(file doesn't exist), catch the exception and set summaryBeforeExec to null.
This eliminates the race condition between checking and reading the tmpSummary
file.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 56ddc82e-cf78-4f0b-a479-54dd3b66075a
📒 Files selected for processing (6)
library/qa/repo-sweep/c3/security.mdsrc/hooks/spawn-wiki-worker.tssrc/hooks/wiki-worker-spawn.tssrc/hooks/wiki-worker.tstests/claude-code/wiki-worker-windows.test.tstests/claude-code/wiki-worker.test.ts
✅ Files skipped from review due to trivial changes (1)
- library/qa/repo-sweep/c3/security.md
…le-system-race) CodeQL flagged a check-then-use race on the tmp summary path: existsSync() was checked, then the same path was written/read. Under the stdout pivot the summary already lives in memory, so the disk round-trip was redundant. Drop tmpSummary entirely: capture the sanitized stdout into an in-memory `producedSummary`, use the existing summary (held in memory) as the skip-on-no-change baseline, and upload directly. No existsSync/readFileSync/ writeFileSync on the summary path remains, so there is no race, and the summary is never written to disk at rest. Upload/skip/finalize semantics are unchanged; all wiki-worker tests pass.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/hooks/wiki-worker.ts (1)
289-291: 💤 Low valueConsider using the pre-validated
memoryTablefor consistency.Line 153 validates
cfg.memoryTableintomemoryTableviasqlIdent(), but line 290 passes the rawcfg.memoryTabletouploadSummary. For defense-in-depth consistency with the validation pattern established earlier in this function, consider passing the validated identifier.Per the review stack context,
upload-summary.tswas hardened in the previous layer and likely validates internally, so this is a minor consistency observation rather than a security gap.Suggested change
const result = await uploadSummary(query, { - tableName: cfg.memoryTable, + tableName: memoryTable, vpath, fname,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/wiki-worker.ts` around lines 289 - 291, In the `uploadSummary` function call around line 289-291, replace the raw `cfg.memoryTable` value passed to the `tableName` parameter with the pre-validated `memoryTable` variable that was created earlier in the function via `sqlIdent()` at line 153. This ensures consistency with the validation pattern established earlier in the same function.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/hooks/wiki-worker.ts`:
- Around line 289-291: In the `uploadSummary` function call around line 289-291,
replace the raw `cfg.memoryTable` value passed to the `tableName` parameter with
the pre-validated `memoryTable` variable that was created earlier in the
function via `sqlIdent()` at line 153. This ensures consistency with the
validation pattern established earlier in the same function.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 845f323a-0c85-4180-ab7c-76edc23d507e
📒 Files selected for processing (1)
src/hooks/wiki-worker.ts
- pack-check.mjs: make the secret-file patterns case-insensitive so
ID_RSA / Credentials.JSON / prod-cert.PFX / .ENV can't bypass the
publish gate on case-insensitive filesystems (Major).
- grep-core.ts + deeplake-fs.ts: wrap sqlIdent() table identifiers in
double quotes ("${sqlIdent(...)}") to match the convention used in
virtual-table-query.ts / upload-summary.ts / mcp/server.ts / wiki-worker.ts
and preserve PostgreSQL identifier case semantics. These two files were
the only sqlIdent sites left unquoted (Major). Test assertions reverted
to the quoted form accordingly.
- vfs-handler.test.ts: assert the stable "Failed to parse snapshot" prefix
instead of the generic substring "parse" (Minor).
Full suite green (the cli-index/cli-update failures are pre-existing
parallel-isolation flakes that pass in isolation and on CI).
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/shell/deeplake-fs.ts (1)
453-464:⚠️ Potential issue | 🟠 MajorApply
sqlIdent()uniformly across all table identifier interpolations for defense-in-depth consistency.Lines 453 and 464 correctly use
sqlIdent()to protect table identifiers, and string values are properly escaped viaesc()at lines 439–445. However, the same hardening is missing from 18 other query sites in this file that still use raw"${this.table}"or"${this.sessionsTable}"interpolation:
Location Query type Line 222 Bootstrap memory SELECT Line 263 Bootstrap sessions SELECT Lines 639, 654 Prefetch queries Lines 687, 698, 746, 756 Read queries Line 721 Index.md check Line 835 appendFile UPDATE Lines 1078, 1081 rm DELETE While table names are configuration-driven (not user input), applying
sqlIdent()uniformly across all 22 query sites eliminates inconsistency and strengthens defense-in-depth.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/shell/deeplake-fs.ts` around lines 453 - 464, Apply the sqlIdent() function uniformly to all table identifier interpolations throughout the file for consistency and defense-in-depth. Currently, the UPDATE and INSERT queries at lines 453 and 464 correctly use sqlIdent() for table names, but raw string interpolation of this.table and this.sessionsTable remains at 18 other locations including the bootstrap memory SELECT query at line 222, bootstrap sessions SELECT at line 263, prefetch queries at lines 639 and 654, read queries at lines 687, 698, 746, and 756, the Index.md check at line 721, the appendFile UPDATE at line 835, and the rm DELETE queries at lines 1078 and 1081. Replace all occurrences of "${this.table}" with "${sqlIdent(this.table)}" and "${this.sessionsTable}" with "${sqlIdent(this.sessionsTable)}" at each of these locations to ensure consistent protection of table identifiers across all query sites in this file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/shell/deeplake-fs.ts`:
- Around line 453-464: Apply the sqlIdent() function uniformly to all table
identifier interpolations throughout the file for consistency and
defense-in-depth. Currently, the UPDATE and INSERT queries at lines 453 and 464
correctly use sqlIdent() for table names, but raw string interpolation of
this.table and this.sessionsTable remains at 18 other locations including the
bootstrap memory SELECT query at line 222, bootstrap sessions SELECT at line
263, prefetch queries at lines 639 and 654, read queries at lines 687, 698, 746,
and 756, the Index.md check at line 721, the appendFile UPDATE at line 835, and
the rm DELETE queries at lines 1078 and 1081. Replace all occurrences of
"${this.table}" with "${sqlIdent(this.table)}" and "${this.sessionsTable}" with
"${sqlIdent(this.sessionsTable)}" at each of these locations to ensure
consistent protection of table identifiers across all query sites in this file.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 6895f7aa-735b-41e0-9104-b97c127ac47a
📒 Files selected for processing (4)
scripts/pack-check.mjssrc/shell/deeplake-fs.tssrc/shell/grep-core.tstests/shared/graph/vfs-handler.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/shared/graph/vfs-handler.test.ts
…t review Brings the focused-PR (activeloopai#271) hardening back into the full sweep so activeloopai#270 is the single shippable PR (it correctly keeps the approved cursor relocation from activeloopai#268). Changes: CodeQL Critical (auth.ts): the OAuth browser opener fed a server-derived verification URL into `cmd /c start` on Windows (cmd re-parses its own command line). Now validate the URL (https scheme, parsed) and open via `rundll32 url.dll,FileProtocolHandler` with no shell interpreter. CodeQL High (tests): cli-install-cursor-fs.test.ts creates its temp root with mkdtempSync instead of a predictable tmpdir join. Wiki-summarizer prompt-injection blast radius (Option B, full fix): pivot the Claude summarizer to the stdout model. Session transcript + existing summary are inlined into the prompt as untrusted DATA (delivered over stdin), the agent emits the summary to stdout, and the worker sanitizes (control-char strip + 100k cap) and uploads it from memory. CLAUDE_FLAGS drops bypassPermissions and all --allowedTools, so the agent has zero tools. No tmp summary file is written or read back, removing the file-system race too. CodeRabbit review: pack-check secret patterns are case-insensitive (ID_RSA / Credentials.JSON / .ENV can't bypass the publish gate); grep-core.ts and deeplake-fs.ts wrap sqlIdent() identifiers in double quotes to match the codebase convention and preserve PostgreSQL case semantics; vfs-handler test asserts the stable "Failed to parse snapshot" prefix. Full suite green (cli-update failures are a pre-existing parallel-isolation flake that passes in isolation and on CI).
|
Superseded by #270. #270 is the better vehicle: it correctly keeps the approved cursor relocation from #268 (which this PR had reverted), and it now carries the same hardening that was developed here, the auth.ts command-injection fix (rundll32 + https validation), the temp-file mkdtemp fix, the wiki-summarizer stdout pivot (Option B), the sqlIdent quoting + case-insensitive pack-check + vfs assertion from CodeRabbit. CodeQL is green on #270 (the remaining extension credential-flow Mediums are by-design warnings, not blocking). |
Summary
A lean, security-only cut of the sweep from #270, rebased onto
main. Per @khustup2's review, this drops the cursor extension, the bundle relocation, and the doc/PRD churn so the change is focused and CI is green (CodeQL + full test suite). 85 files, allsrc/,tests/,scripts/pack-check.mjs, and the per-chunk audit reports.What's fixed
SQL identifier hardening - config-driven table names (
HIVEMIND_TABLE/HIVEMIND_SESSIONS_TABLE) wrapped insqlIdent()at every interpolation site:src/deeplake-api.ts,src/mcp/server.ts,src/shell/deeplake-fs.ts,src/shell/grep-core.ts,src/skillify/*, and ~20 sites acrosssrc/hooks/*.src/commands/session-prune.ts(Critical): unescaped table names inDELETEagainst the PII-bearing sessions/memory tables.Credential + filesystem hardening
src/hooks/spawn-wiki-worker.ts(+ codex/cursor/hermes forks): stage the token-bearingconfig.jsonviamkdtempSync(unpredictable, atomic) +chmodSync 0o700, closing the predictable-tmpdir TOCTOU window; file is written0o600.src/hooks/query-cache.ts: sanitizesession_idbefore using it as a path segment.src/graph/vfs-handler.ts: hex-shape guard on snapshot ids to prevent traversal out ofsnapshots/.scripts/pack-check.mjs: publish secret gate now blocks*.pem/*.key/id_rsa/credentials.json.Command-injection fix (CodeQL Critical,
src/commands/auth.ts)cmd /c starton Windows, which re-parses its own command line. Now the URL is validated (https scheme, parsed) and opened viarundll32 url.dll,FileProtocolHandlerwith no shell interpreter. macOS/Linux paths unchanged.Insecure-temp-file fix (CodeQL High)
tests/cli/cli-install-cursor-fs.test.ts: temp root created withmkdtempSyncinstead of a predictabletmpdirjoin.Quality
catch (e: unknown)narrowing acrosssrc/notifications/*, atomic summary writes insrc/skillify/skill-writer.ts, Python extractor node-id dedup insrc/graph/extract/python.ts.Test plan
tsc --noEmitcleannpx vitest run- 4501/4501 passSummary by CodeRabbit
Security Fixes
Bug Fixes
Documentation
Tests