Expand description
candor-classify — the curated effect classifier (crate+path -> effect), extracted to a STABLE
crate so both the nightly rustc_private lint AND a stable backend share ONE source of truth
(no drift). Pure string logic; no rustc internals. The effect vocabulary lives in candor-report.
Modules§
- policy
- The canonical CANDOR_POLICY DSL parser (SPEC §6.2), shared by the nightly gate and candor-query. The canonical CANDOR_POLICY DSL parser (candor-spec SPEC §6.2).
Constants§
- CALIBRATED_
CRATES - The exact third-party crates
classifyhas effect rules for, and the crate-name PREFIXES it recognizes. This is the single source of truth for “what candor knows”: it is emitted beside the JSON report (<prefix>.calibrated.json) so the Claude Code receipt’s coverage check reads candor’s real coverage instead of a hand-copied list. Keep in lockstep withclassifybelow — thedb_crates_are_calibratedandcalibrated_crates_are_livetests (in this crate’stestsmodule) enforce both directions. - CALIBRATED_
PREFIXES - CALIBRATION_
PROBE_ TAILS - Representative path tails (each appended to a crate name) that the
calibrated_crates_are_liveliveness test probes: at least one must match for everyCALIBRATED_CRATESentry, else the entry is dead. Exported as ONE source of truth because the nightly lint crate (src/lib.rs) runs the SAME liveness test — when the two probe lists were duplicated they drifted, and a rule keyed on a distinctive tail (pnet::datalink::channel, ignore::WalkBuilder::build_parallel, notify::RecommendedWatcher::new) added to only one list silently broke the other crate’scargo test. - DB_
CRATES - Database client crates whose execution verbs are I/O (see the DB branch in
classify). Module-level sodb_crates_are_calibratedcan enforceDB_CRATES ⊆ CALIBRATED_CRATES. - PATH_
CALIBRATED_ CRATES - Crates
classifymatches by PATH prefix rather than crate-name equality (their effectful modules are recognised, e.g.tokio::net::/async_std::fs::/mio::net::), so they’re absent fromCALIBRATED_CRATES(which the liveness test probes by crate name). The coverage check must still treat them as covered — otherwise it would mislabel the most common async crates as blind spots.
Functions§
- cap_
from_ name - capstd_
cap - Map a cap-std capability type to the effect it authorises. Holding one of these
(e.g.
&Dir) is the real, unforgeable right to perform that effect — so candor treats it as a declared capability, exactly like its own&Fstoken. - classify
- Classify a resolved callee by the crate it belongs to and its full path.
- classify_
command_ head - Refine the
Execcliff (spec §4 ⟨0.5⟩): the effects a literal, statically-known subprocess head implies, matched by basename (/usr/bin/curl→curl). The head’s effects are ADDED to a caller that already carriesExec(a subprocess is still spawned —Execis never dropped); an unrecognised or dynamically-built head returns&[]and keeps the bare cliff (never guess). A candor engine readsFs/Envonly — spec §7 item 12 (the analyzer self-boundary) guarantees that, so that case is spec-supplied, not curation. The rest is a small curated table under the same under-report rule as the crate classifier. INVARIANT: every head here is an external tool that does NOT run the analysed project’s own code (somake/npm/cargoare deliberately absent — they stay the cliff). The reference engines share this table so theExecboundary — the one boundary every engine hits — refines identically (the §4-consistency argument). - classify_
extra - Project-supplied rules, consulted only when the built-in
classifyreturns None. - is_
cmd_ builder_ method - Whether a subprocess-builder method only MODIFIES the command (
.arg,.env,.current_dir) rather than NAMING the program (Command::new,duct::cmd). A WHOLE-CRATE-Exec crate (portable_pty,duct,async_process) classifies every method asExec, so the head-refinement must skip these: an arg or env-var-name literal that happened to match a head (.env("psql", …),.arg("curl")) would FABRICATE that effect — the §1 under-report rule. The method is the call path’s last segment. - is_
cmd_ naming_ method - Whether a subprocess method NAMES the program (so its first string literal IS the command head to
refine):
Command::new("curl"),duct::cmd("curl", …). The head-refinement must fire ONLY here — an ALLOWLIST, not “any method except known modifiers”. A whole-crate-Exec crate classifies EVERY method asExec, so a denylist leaked NON-naming methods that aren’t modifiers — a getter likeCommandBuilder::get_env("psql")(reading back an env-var KEY, not a program) fed"psql"to the head classifier and FABRICATEDDb(review find). Onlynew/cmdname a program; everything else (modifiers, gettersget_*, custom builder methods) keeps the bareExeccliff — under-refine (safe) rather than fabricate.std::process::Commandis verb-precise so getters never fireExecthere anyway; the allowlist makes the whole-crate-Exec crates safe too. - tables_
in_ sql - Table names a SQL string literal STATICALLY reaches — the
Dbanalog of theNethost /Execcommand /Fspath literal surface (feedsallow Db in <scope> <table>…, AS-EFF-008). Conservative by construction, because a wrong capture here would FABRICATE: the string must open with a SQL statement keyword, and only identifiers in table position are taken —FROM/JOINanywhere,INTOanywhere, statement-leadingUPDATE/TRUNCATE, andTABLE(create/drop/alter), skippingONLY/IF NOT EXISTS.UPDATEmid-statement is deliberately ignored (FOR UPDATE SKIP LOCKEDmust not yield a table “skip”). A dynamically-built query yields nothing — the gate’s opaque case — never a guess. Output is lower-cased, quote/backtick-stripped,schema.tablekept qualified, deduped. SPEC §2 pins this algorithm token-for-token across engines; the cross-impl vector battery (candor-spec conformance/tables/vectors.json, run.sh Part 4b) enforces the JVM/TS mirrors.