Skip to content

Audit remediation: SQL-literal injection escaping (lockstep with DID-python)#146

Merged
4 commits merged into
VH-Lab:mainfrom
audriB:audit/did-matlab-2026-06
Jun 14, 2026
Merged

Audit remediation: SQL-literal injection escaping (lockstep with DID-python)#146
4 commits merged into
VH-Lab:mainfrom
audriB:audit/did-matlab-2026-06

Conversation

@audriB

@audriB audriB commented Jun 12, 2026

Copy link
Copy Markdown

MATLAB half of the DID lockstep (audit §6.1). Merge together with VH-Lab/DID-python#23. For review — please do not merge without sign-off.

⚠️ Author-not-run: authored without a local MATLAB runtime. Please run the DID-matlab + symmetry suites before merging.

Changes

  • SQL-injection escaping (§6.1-3): several sqlitedb queries interpolate branch_id/doc_id/document_id/filename into double-quoted SQL string literals; run_sql_query() doesn't forward bind params to mksqlite, so those values can't be ? placeholders and a value containing " could inject. Added a private escapeSqlLiteral (doubles ", since SQLite reads "" as an escaped " inside a double-quoted token), mirroring DID-python's _sql_escape, applied at all 14 value-interpolation sites. The already-parameterized run_sql_noOpen('... =?', val) calls are unchanged.
  • isa (§6.1-1) needed no MATLAB change (MATLAB is the correct reference; Python caught up). MATLAB already has the sqlite indexes (§6.1-4).

Deferred — DECISION required (not changed)

  • timestamp column format (§6.1-2 / §7.3-13): datenum-days (MATLAB) vs epoch-seconds (Python) in the same column → cross-client comparisons break. Cross-client decision for DID-matlab/DID-python/cloud backend jointly. See docs/Audit_Remediation_Results_2026-06-12.md.

…rals (audit 6.1-3)

Several sqlitedb queries interpolate branch_id / doc_id / document_id /
filename directly into SQL string literals delimited by double quotes, e.g.
['... WHERE doc_id="' document_id '"']. run_sql_query() does not forward bind
parameters to mksqlite (it calls do_run_sql_query without varargin), so these
values cannot be passed as '?' placeholders; a value containing a double quote
could break out of the literal and inject SQL (e.g. a crafted branch name or
document id).

Add a private escapeSqlLiteral helper that doubles embedded double quotes
(inside a double-quoted token SQLite reads "" as an escaped "), mirroring the
DID-python _sql_escape (which doubles single quotes for its single-quoted
literals), and apply it at all 15 value-interpolation sites
(do_add_branch / do_delete_branch / do_get_branch_parent / do_get_sub_branches
/ do_get_doc_ids / read / remove_doc / open_doc / file path lookups). The
existing parameterized run_sql_noOpen('... =?', val) call sites are unchanged.

Lockstep with the DID-python field-name validation (same audit item 6.1-3).

Authored without a local MATLAB runtime; needs MATLAB to validate/run.
Maps the §6.1 finding addressed here (SQL-literal escaping, 6.1-3) to its
commit, notes that isa (6.1-1) and the sqlite indexes (6.1-4) needed no MATLAB
change (MATLAB is the correct reference / already has them; Python caught up),
and records the timestamp-format DECISION (§6.1-2/§7.3-13: datenum vs epoch
seconds in the same column) as a cross-client choice deliberately NOT changed
here, pending joint sign-off. Reiterates author-not-run + the lockstep merge
with audit/did-python-2026-06.
Adversarial review counted 14 escapeSqlLiteral call sites in sqlitedb.m, not 15
(the earlier count of 15 included a docstring-example occurrence that was later
reverted). All vulnerable double-quoted interpolations are still covered.

Copy link
Copy Markdown
Contributor

Superseded by #148, which has been merged into main.

#148 carries this PR's SQL-literal injection escaping (audit §6.1-3) verbatim, brought onto a VH-Lab branch so it could be edited and run through CI directly. It additionally fixes the unrelated codecheckToolbox CI failure (the bare-named wrapper didn't accept the new check-code@v1 args, and badge generation needs CPython on the runner) by making the wrapper accept/forward the args with CreateBadge=false. All checks passed and it's merged.

The deferred timestamp column format decision (datenum-days vs epoch-seconds, §6.1-2 / §7.3-13) remains open and is catalogued in #147.

Closing this one as superseded; thanks @audriB. The Python half of the lockstep (DID-python#23) is next.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants