Skip to content

Fix the findings of the post-audit security review and make the route search faster - #131

Merged
0xfauzi merged 7 commits into
mainfrom
ccr-01549f8f-6750vr
Oct 27, 2026
Merged

0xfauzi merged 7 commits into
mainfrom
ccr-01549f8f-6750vr

Conversation

@0xfauzi

@0xfauzi 0xfauzi commented Oct 27, 2026

Copy link
Copy Markdown
Owner

This change follows #128, #129 and #130. A second security review examined the code that the audit fixes added. This change fixes the confirmed findings of that review. It also makes the strict route search faster.

Security review of the new code

Three finders examined the new code, and a verifier for each finder tried to disprove each finding. There are 17 findings, and the verifiers disproved none. All are low or info severity.

Fixed, each with a regression test that fails on the old code:

  • git-sec-1: a promisor remote in .git/config started a lazy fetch during a read. Each Git command now gets GIT_NO_LAZY_FETCH=1 and an empty GIT_ALLOW_PROTOCOL.
  • git-sec-2: a fork PR could change out_dir or the figure paths in systemap.toml, and the base-render job then did not check the figures. The job now refuses a PR that changes those settings, and render --check also compares the figure files that exist.
  • post-server-1: a page of a different web site could fill the 32 /events slots. The servers now refuse a cross-site GET request for /events.
  • post-server-2: systemap serve copied the CSP meta content of a served file into a response header without a check. The value is now checked.
  • post-server-4: the served pages now have frame-ancestors 'none'.
  • SEC-1: the tracked-cache refusal failed open when Git could not list the index. It now refuses the cache file.
  • SEC-2: the fact cache could keep the facts of one read with the digest of a different read. The cache now parses the same text that it digests.
  • SEC-3: facts_shape now refuses a stored module without a sha value.
  • SEC-4, SEC-5, SEC-8: newlines, control characters and bidirectional controls in stored names cannot make a false line, and the terminal output shows them as escapes.
  • SEC-6: decisions submit refuses a deeply nested results file with an error, not a RecursionError.

Not fixed, with the traced reason:

  • git-sec-3: on Windows, core.hooksPath=nul is a relative path. A Windows measurement is necessary before a change.
  • git-sec-4: the pre-commit hook pins are tags, because .pre-commit-config.yaml must stay byte-identical with its copies. The pin belongs in the source of the copies.
  • git-sec-5: the scaffold template pins uv and systemap. systemap has no other dependencies.
  • post-server-3: no current sink exists.
  • SEC-7: plausible only. It needs a file system with a coarse change time.

Route search

route_astar.py adds an A* search with an admissible cut bound. The routes are byte-identical on this repository and on the s36, s72 and s144 fixtures, and the figures do not change.

route_all, 3 runs each:

Map Before After
s72 2.39-2.43 s 0.95-1.10 s
s144 9.00-9.28 s 4.18-4.25 s

The time still grows with the square of the number of flows (exponent 1.93-2.16). The target was less than 4 s at s144, and this change does not satisfy it.

Checks

These checks pass on the head:

  • uv run pytest: 2487 passed, 7 skipped
  • uv run pre-commit run --all-files
  • uv run mypy src --strict
  • uv run systemap refresh
  • uv run systemap check
  • uv run systemap judgement: no open findings, 16 current reviews

The official ASD-STE100 dictionary was not available. A maintainer must examine the words of the new text.


Generated by Claude Code

Audit finding scale-5. A strict search now uses route_astar.astar. The
bound adds, for each row and column interval between a node and the
target nodes, the lowest cost of an allowed segment; the router counts
the unused segments of each interval. The search keeps the tie rules of
the Dijkstra search and the exact float distances, so the routes are
byte-identical to 154e395 on this repository, s36, s72, s144, perf/huge,
the pages and figures, and 2900 random layouts.

The router also finds the regions of all segments one time with bisect
and keeps the regions of each port stub.

route_all on s144: 9.00 to 9.28 s before, 4.18 to 4.25 s after. The
acceptance value (under 4 s, exponent under 2) is not reached; see
bench/jev/README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
- git-sec-1: each Git process gets GIT_NO_LAZY_FETCH=1 and an empty
  GIT_ALLOW_PROTOCOL, also the cat-file process of delta. A promisor
  remote cannot run its upload program during a read.
- git-sec-2: base-render refuses a fork pull request that changes
  out_dir, model or figures. render --check also compares each figure
  file that exists.
- post-server-1: the servers refuse an event connection from a
  different site before it holds a slot.
- post-server-2: of_page gives a policy only if it is printable ASCII.
  A line break in the meta content gives no header.
- post-server-3: the page_csp docstring tells which injection the
  policy stops. No code change.
- post-server-4: the header policy has frame-ancestors 'self', and the
  servers send X-Content-Type-Options: nosniff.
- SEC-1: in a work tree, a Git error in the tracked-cache check refuses
  the cache file, with the Git cause.
- SEC-2: remember_file keeps a value only if the digest after make is
  the same as the key (modules and test files).
- SEC-3: a module record must have sha.
- SEC-4: the facts shape refuses a control character in a module key,
  a file path and an entry point name.
- SEC-5: the cache warning shows control characters as escapes.
- SEC-6: a deep results file gives DecisionError.
- SEC-7: the racy test uses the later of mtime and ctime.
- SEC-8: printable escapes the bidirectional format characters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
Merge results of fix/post-router and fix/post-security:

- Refresh docs/map for the merged source.
- Correct the source records of 28 flows whose cited modules changed
  (check, cli, route), and record their review digests after an
  examination of each flow claim.
- Correct two claims: the CLI page servers also refuse a GET request for
  /events from a different web site, and SecondOpinion does not use a
  cache file in a repository if Git cannot tell if it tracks the file.
- Record the source reviews of 16 components after an examination of
  their claims. No other claim text became false.
- Add the CHANGELOG lines for the post-audit fixes under Unreleased.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
@github-actions

Copy link
Copy Markdown

Architecture

  • Contract: no change
  • Map: 3 changed components
    • changed component CLI (description): examined, source review current
    • changed component Router (modules): examined, source review current
    • changed component SecondOpinion (description): examined, source review current
  • Rules: none

Levels: established = the facts show the claim. examined = an agent or a person examined the cited source. unknown = the facts and the source reviews do not show the claim.

Windows refuses control characters in a file name, so a cache file with
ESC and BEL in its name cannot exist there, and the test could not make
its input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
@0xfauzi
0xfauzi merged commit de31fe3 into main Oct 27, 2026
19 checks passed
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.

2 participants