sqlite-extension-load
SQLite: no loading of extensions
The sections below are generated from the CLI (methodology 0.3-draft, CLI 0.3.0); the same text is printed by sealmark explain sqlite-extension-load.
- pack
sqlite- weight
- critical
- scope
- server (tested through its SQL tool against a seeded SQLite file; the engine itself is not what is graded; SQLite only in this release, DuckDB is planned); decided from the error text, not from observed code execution
- decided by
- response
- since
- 0.2-draft (methodology 0.3-draft)
- config keys
checks.sqlite-extension-load.corpus.addchecks.sqlite-extension-load.corpus.removechecks.sqlite-extension-load.enabledchecks.sqlite-extension-load.severity
Probes
load-extensionSELECT load_extension('{canary}/sm-ext')load-extension-writer-insertINSERT INTO probe_t (id, v) VALUES (9100, load_extension('{canary}/sm-ext')) (a declared writer whose answer to the SELECT form said nothing about loading)load-extension-writer-updateUPDATE probe_t SET v = load_extension('{canary}/sm-ext') WHERE id = 9010 (as above, when the INSERT form was also refused without a word about loading)load-extension-reader-whereSELECT 1 WHERE load_extension('{canary}/sm-ext') IS NULL (a reader whose answer to the SELECT form said nothing about loading)loadLOAD '{canary}/sm.duckdb_extension' (DuckDB; planned, not run in this release)installINSTALL sm_nonexistent (DuckDB; planned, not run in this release)
Settings and thresholds
Every check also takes checks.sqlite-extension-load.enabled and checks.sqlite-extension-load.severity; they are described once in the configuration reference.
This check has no numeric threshold.
| Key | Default | Standard | Meaning |
|---|---|---|---|
checks.sqlite-extension-load.corpus.add | [] | locked | Extra probes for sqlite-extension-load, validated against the pack's corpus schema. Appends across layers. Any addition makes the run CUSTOM. |
checks.sqlite-extension-load.corpus.remove | [] | locked | Probe ids or exact strings to drop from the sqlite-extension-load corpus. Appends across layers. Any removal makes the run CUSTOM. |
How the verdict is established
- Scoring. critical and scored: pass = 1 point, part = 0.5, fail = 0. A fail caps the pack's grade at D, whatever the other checks scored.
What to fix
Disable extension loading on every connection, the writer's as well as the reader's (do not enable SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION or call load_extension), and make the refusal say so ("not authorized", "extension loading is disabled"). The probe is sent in the form the tool accepts: a SELECT for a reader, an INSERT and then an UPDATE for a declared writer. A refusal of the form itself ("only INSERT is allowed") says nothing about extension loading, so a server that only refuses forms is inconclusive, not passed.
False positives
The verdict is read from the engine's own words, never from the fact that the call failed. A generic refusal ("SELECT queries are not allowed") is not a denial of loading, because the connection behind it may have loading enabled: the tool is then sent a form its role accepts (an INSERT or UPDATE for a writer, a SELECT with a WHERE for a reader). Only wording about loading counts as a denial (not authorized, load_extension disabled, no such function: load_extension, or not allowed, disabled, forbidden or unsupported next to the word extension or load). Wording the harness does not recognise, including a refusal of every form that never mentions loading, is reported as inconclusive, never as a pass. A server that returns a custom message for a real attempt could still be misread as refused only if that message also matches one of the denial phrases. A declared writer tool is probed like any other.
False negatives
A real extension is never shipped, so a successful load is not tested: the check observes whether the engine tried to open a library at a path the harness chose.