Skip to content

feat(guard): the build refuses placeholders, fabricated results, boolean literals and hidden errors - #1045

Merged
cryptskii merged 1 commit into
mainfrom
feat/no-fakes-compile-guard
Sep 27, 2026
Merged

cryptskii merged 1 commit into
mainfrom
feat/no-fakes-compile-guard

Conversation

@cryptskii

Copy link
Copy Markdown
Collaborator

What

The build now refuses placeholders, fabricated results, boolean literals and hidden errors. Nothing waits for GitHub: every Rust crate's build script, the Android preBuild and the frontend's start, build, type-check and test scripts run scripts/real_code_guard.py over their own sources, and a source line holding a forbidden token fails the build unless the baseline already records that exact line.

Refused:

  • true and false, and their spellings as constants (bool::default(), !0, !!1, Boolean(1), 1 == 1).
  • Placeholder, stub, fake, dummy and mock; TODO/FIXME/HACK; notes that the code does the lesser thing ("for now", "in a real implementation", "simulated", "stand-in").
  • Zero 32-byte values, Ok(vec![]), todo!()/unimplemented!().
  • Every way of hiding an error: unwrap_or*, .ok(), map_err(|_|), Err(_), is_ok()/is_err(), catch_unwind, an error downgraded to a log line, runCatching/getOrNull, an empty or unbound catch, .catch(() =>, void promises, invented ??/|| defaults, "non-fatal"/"best-effort" notes.
  • Every switched-off check: #[allow], @Suppress, @ts-ignore, eslint-disable, as any, ignored or skipped tests, testing features, jest test doubles.

No way around it:

  • The baseline (scripts/real_code_baseline.txt, 10,211 occurrences that predate the guard) only shrinks. The guard refuses a baseline that gains an entry main does not hold, and --write-baseline can only remove entries once main holds one.
  • The guard refuses to run when it differs from main's guard: only the owner changes it.
  • Every run checks the wiring: a crate whose build.rs stops calling the guard (or whose Cargo.toml overrides its build script), a missing Gradle task or npm pre-script, a CI job or hook that stops running it, fails whichever build still runs the guard.
  • A new CI job, Real-code guard, runs on every change: the self-test and the whole tree against main. It is not a required check until branch protection names it.
  • The shared build snippet is itself scanned. The guard's own name was the first thing it caught (no_fakes_guard holds "fakes"), so it is real_code_guard.

CLAUDE.md (git-ignored, updated in both this checkout and the main one) now states the rule and that violations are penalized with fines.

Verification

  • Self-test: 27 rules, 0 failures (each rule refuses its sample and passes a clean line). Whole tree: clean against the baseline, 1.3 s.
  • Blocks, proven by adding the line and building: a literal true → cargo check -p dsm_sdk exit 101; false in a Kotlin file → :app:realCodeGuard fails preBuild; !0 in a TS file → npm run type-check and npm run build fail; .ok(); → cargo check -p dsm_storage_node exit 101; dsm-sphincs's guard call removed → the storage node's build and make lint fail on the wiring check. Each probe removed; builds clean again.
  • Against the committed reference: a baseline entry added → refused ("the baseline gained 1 entry … that HEAD's baseline does not hold"); the guard edited → refused ("differs from HEAD's guard"); a new literal laundered through --write-baseline → still refused.
  • make lint: passed. The Pico firmware, the anchor library, the hw verifier and the guarded-reference tool build through their guarded scripts.

…ean literals and hidden errors

Every Rust crate's build script, the Android preBuild and the frontend's
start, build, type-check and test scripts run scripts/real_code_guard.py over
their own sources. A source line that holds a forbidden token fails the build
unless the baseline already records that exact line. Forbidden: the literals
true and false and their spellings as constants; placeholder, stub, fake,
dummy and mock, TODO/FIXME/HACK and notes that the code does the lesser
thing; zero 32-byte values, empty Ok answers, todo!/unimplemented!; every way
of hiding an error (unwrap_or*, .ok(), map_err(|_|), Err(_), is_ok/is_err,
catch_unwind, an error downgraded to a log line, runCatching/getOrNull, an
empty or unbound catch, .catch(() =>, void promises, invented ?? / || defaults,
non-fatal / best-effort notes); and every switched-off check (allow, Suppress,
ts-ignore, eslint-disable, as any, ignored or skipped tests, testing features,
jest test doubles).

The baseline holds the 10,211 occurrences that predate the guard and only
shrinks: the guard refuses a baseline that gains an entry main does not hold,
and a guard that differs from main's. Every run also checks the wiring, so a
crate that stops calling the guard, a Gradle task or npm pre-script that goes
missing, or a CI job that stops running it fails whichever build still runs.
A CI job on every change runs the self-test and the whole tree against main.

Verified: self-test 27 rules, 0 failures; whole tree clean against the
baseline; a literal true blocks cargo check (dsm_sdk), a literal false blocks
the Android preBuild, !0 blocks the frontend type-check and build, .ok();
blocks the storage node, and removing dsm-sphincs's guard call blocks the
storage node's build and make lint. make lint passes; the Pico firmware, the
anchor library and the hw verifier build through their guarded scripts.
@cryptskii
cryptskii merged commit 9020f12 into main Sep 27, 2026
24 of 25 checks passed
@cryptskii
cryptskii deleted the feat/no-fakes-compile-guard branch September 27, 2026 22:09
cryptskii added a commit that referenced this pull request Sep 28, 2026
…ile transfers as a real send does

Docker: every Rust crate's build script runs scripts/real_code_guard.py. The
storage-node builder image had no python3, so `docker build` of the node has
failed on every Rust change since the guard landed (#1045). The builder now
installs python3, and the guard runs inside the image as it does everywhere
else.

CodeQL alert #732 (rust/hard-coded-cryptographic-value): the dispatch tests
signed their hostile sender's transfers on nonces written as constants.
- The transfer-nonce derivation moves out of process_online_transfer_logic
  into transfer_nonce(), which the send path now calls.
- The tests derive their nonces with it from A's real relationship tip.
- They build each transfer as A's send builds one: `to` in Base32, and
  `recipient` the key A holds.
- The "another device" transfer is addressed to A itself, not to an invented
  id.
The mutation controls on the three affected tests (the recipient check, the
child-tip bind, and conflicts left to the apply) were run again, and all three
are red.
cryptskii added a commit that referenced this pull request Sep 28, 2026
Main's Docker job has been red since #1045: every crate's build script now runs
scripts/real_code_guard.py through python3.

- ci/docker/StorageNode.Dockerfile installs python3 (the image copies the whole
  workspace, so the guard has everything else).
- dsm_storage_node/Dockerfile.cloud, the image deploy/push_and_start.sh builds,
  could not find the guard at all: it copied selected folders and built against
  stand-ins (fn main(){} crates, invented manifests for tools that no longer
  exist, empty lib.rs files) behind a cargo build whose failure was thrown away
  (2>/dev/null || true). Its builder now installs python3, copies the whole
  workspace as the CI image does, and builds --locked on rust:1.98, the pinned
  toolchain. The runtime stage is unchanged.

Both images build locally and their binaries answer --help. Gemini (gate round
1) was satisfied.
cryptskii added a commit that referenced this pull request Sep 28, 2026
… ingestion boundary (#1048)

* fix(online-transfer): the signed operation is the only authority; one ingestion boundary

The recipient recorded a received transfer's amount and token from
OnlineTransferRequest fields SIG A does not cover (H-B1), and the proposed
refusal of mismatched copies froze a transfer for good: a copy that differed
outside the signed bytes staged first, the honest copy was refused, and the
send barrier held. The drift behind it: the same fact lived in two places,
and transport bytes decided durable state. No unverified storage-supplied
field now becomes a durable acceptance, refusal, correlation or liveness
dependency.

Wire: OnlineTransferRequest keeps SIG A, its canonical operation and the two
locator hints; fields 1-4, 6, 7 and 12 are reserved by number and name.
ReceiptEvidenceA keeps only the receipt. The Evidence.oracle key the receiver
ignored is gone.

Recipient: one ingestion boundary (handlers/recipient_dispatch.rs): parse,
candidate identity as a hint, verify under the stored key, sender = the
contact whose key verified, recipient check, object key, stage, bind by the
recomputed child tip. Recognition answers either a verdict about the half or
that this device cannot decide (its store or its own id unreadable); only the
second is an error. Three identities kept apart: the object (a transfer by
its signed operation bytes, a receipt by its commitment), the step
((relationship, parent) and the nonce, decided only by the canonical apply),
and the observation (address, observed message id, locator hints: dedup and
collection only). Staging (schema 26) holds recognized objects; the barrier
reads the verified sender; a copy of an accepted receipt is recognized by its
commitment. The pair is accepted inside the apply's transaction and carried
forward as the store holds it; the history row, the inbox listing and
wallet.history read the signed operation; the credit takes the signed policy
commitment. The inbox lists a copy it cannot recognize as unverified, with
no sender.

Sender: process_online_transfer_logic takes an OnlineSendIntent; the
wallet.send app route (called only by the test harness, and taking the
sender id from its caller) is deleted; the harness sends through
wallet.sendSmart and syncs through the router with the poller's own request.

Core: the acceptance bundle's transfer leg carries SIG A and its message;
the check of the unsigned field 12 is gone. Published bundles still verify.

Tests drive the boundary with what a hostile sender and anyone who can seal
to the recipient can put on the spool: a second copy under a lying wrapper,
a sender named in a wrapper, junk, a transfer addressed elsewhere, a receipt
that does not verify, a rival signed on the same nonce, a wrong locator hint,
evidence first, copies of a finished transfer, and the inbox listing.

CONFORMANCE_GAPS §6.37 records the pass and what is left open.

* test(online-transfer): a forged SIG A is recorded nowhere; every gate has its mutation control

The ingestion boundary's SIG A check had no negative test of its own. Junk
fails at decode and an unknown sender fails at key lookup, so removing the
check turned nothing red. The new test,
a_transfer_whose_sig_a_does_not_verify_is_recorded_nowhere, posts the sent
request with one signature byte changed. It asserts that the request is not
recognized and that nothing is staged.

VERIFICATION_MATRIX maps the boundary's eleven gates to their named tests:
- SIG A;
- the recipient check;
- the receipt's sig_a chain;
- the child-tip bind;
- the object key;
- consuming every observed id;
- leaving conflicts to the canonical apply;
- the barrier's verified sender;
- recognizing an accepted receipt;
- the inbox's sender;
- acceptance inside the apply's transaction.
Each named test was observed red with its gate removed or weakened, and the
gate was restored afterwards.

CONFORMANCE_GAPS §6.37 names the new tests. Under the message pass, it records
the transfer-only submission fields that message.send fills with empty values.

* ci: the storage-node image runs the real-code guard; tests build hostile transfers as a real send does

Docker: every Rust crate's build script runs scripts/real_code_guard.py. The
storage-node builder image had no python3, so `docker build` of the node has
failed on every Rust change since the guard landed (#1045). The builder now
installs python3, and the guard runs inside the image as it does everywhere
else.

CodeQL alert #732 (rust/hard-coded-cryptographic-value): the dispatch tests
signed their hostile sender's transfers on nonces written as constants.
- The transfer-nonce derivation moves out of process_online_transfer_logic
  into transfer_nonce(), which the send path now calls.
- The tests derive their nonces with it from A's real relationship tip.
- They build each transfer as A's send builds one: `to` in Base32, and
  `recipient` the key A holds.
- The "another device" transfer is addressed to A itself, not to an invented
  id.
The mutation controls on the three affected tests (the recipient check, the
child-tip bind, and conflicts left to the apply) were run again, and all three
are red.

---------

Signed-off-by: Cryptskii <47649969+cryptskii@users.noreply.github.com>
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.

1 participant