Repository navigation
Fix the P2 findings of the security and performance audit - #129
Merged
Merged
Conversation
…s (scale-2) One command renders the same map for the page, each figure, the check and the stale check. Each render calculated route_all, place_labels, assurance.of_model and evidence.of_model again. route.routed() now keeps the routes and the label positions by the repr of all inputs. assurance.of_model and evidence.of_model keep their results by a digest of the pickle of all inputs. Each cache is bounded and gives a copy or frozen records, so a caller cannot change it. On this repository (3 + 5 runs each, frozen copy at 136975f): check 13.14 s -> 6.45 s, refresh (current) 13.18 s -> 6.27 s, refresh (stale) 32.40 s -> 7.60 s. Router.route calls: 400 -> 80 (check), 1120 -> 80 (stale refresh). The written outputs are byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
…pping extraction Findings g1full-3, exec-3 and g1ts-4 of the audit. The TypeScript call-site reader makes one walk from the root with a stack. The walk records the parent, the innermost named function and the binding flag of each node. Each property chain gets its dotted name one time. The scope reader keeps the declaring scope of each name on each path. The TypeScript walks, the syntax tokens, the test names, the binding patterns and the package.json exports targets use a stack, not recursion. If a parse or a walk of one module gives RecursionError or MemoryError, the module stays in the facts with an unknown record of the new cause `depth`. A test file gives a test_file_issues record. The Python tree cache refuses such a text, so later steps do not read its tree. Measured: 20 TypeScript files of 1.8 KB take 1.0 s in systemap extract (before: 120.7 s). The deep fixtures of the audit exit 0. The outputs of this repository are byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
rest-3: python_unresolved calculated the namespace package set again for each module, so the extraction time grew with the square of the module count. The Python context now keeps one set for each extraction (NamespaceCache), and namespace_packages uses str.rfind prefixes. The facts output does not change. g1ts-8 and g1full-6: read_facts now examines the shape of the stored facts (facts_shape): the types of the fields that extraction writes, the keys that the readers index, relative file paths, and a nesting limit of 32 levels. A RecursionError from the JSON parser is also a shape error. An incorrect file gives FactsError (a ConfigError) with exit code 1 and the action "Use systemap refresh or systemap extract to write a new facts file". extract reads the stored facts only for --check, and refresh reports an incorrect file as stale, so the two commands can write a new file in place of an incorrect file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
ci-2: The PR summary builds each gap detail from a fixed form and the repository names. Each name is a code span. A detail without names gets a word joiner after each "@", so it starts no mention. ci-5: The delta step of this repository and of the init workflow writes the comment output with a random heredoc delimiter. A comment line that is equal to the old delimiter does not end the output. ci-1: plugin.json, README.md and the coding agents page do not give "uv run systemap" as a plugin command. They tell the reader not to use it, because it builds the repository project. The trust sentence says that it is not true if the command runs repository code. ci-3: A new pre-commit CI job runs all hooks on all files, and the manual gitleaks-tree hook. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
…rver server-1: The page servers now refuse a POST request with 403 when Sec-Fetch-Site is present and is not same-origin or none, or when Origin is present and is not the served origin. A program without these headers, for example curl or the agent hook, can still send POST /refresh. server-2: Each connection has a 10 s read time limit. The live server accepts at most 32 event connections and refuses more with 503. When accept fails because the process has no free descriptor, the server stops for 0.1 s before the next accept. The new module systemap.serve_guard is part of the CLI component. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
llm-2: _compact split the page script with str.splitlines. A U+2028, U+2029 or U+0085 in map text cut the DETAIL line, and the page script stopped. _compact now splits only at a line feed. The new line_safe function escapes U+2028, U+2029 and U+0085 in the detail JSON and in the workspace JSON of the page. llm-7: journeys.add_to_source mixed str.splitlines offsets with the rows of the tokenizer, and with_imports mixed them with the rows of ast. Both now split the lines as the Python reader does. llm-3: decisions submit now refuses a result with text that UTF-8 cannot encode (for example a lone surrogate) before it writes a file. answer_write changes UnicodeEncodeError into AnswerWriteError before the write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
Add terminal_text.printable. It replaces C0 and C1 control characters, DEL, U+2028, U+2029 and unpaired surrogates with a visible \uXXXX escape. Other text does not change. - cli.say, cli.warn, failures.report, the serve printer and the jev_cli output use terminal_text.lines, which keeps the newline. - Finding lines in judgement and delta reports, commit subjects in history, sequence ids, labels and agent problems in journeys, and request ids and file names in decisions errors use printable. - history divides git output only at the newline. Before, U+2028 in a commit subject gave two commit lines. - journeys refuses an id that is not a slug, an empty id, and a label or step sentence with a control character. - decisions submit refuses a summary with a control character. Finding identifiers do not change. The output of check, judgement, history, decisions and refresh for this repository is byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
…ex-3) Each pattern in answer_migrations now has its fixed text at its fixed position, and each variable part has a limit. A name or a reason in a current line can no longer move the fixed text, and a match is linear in the length of the line. An item that is a current line stays the same: judgement and audit give the current lines to current_line as `present`. An item longer than LONGEST_MIGRATED (4096 characters) stays the same. A cache makes the pattern work one time for each item. Measured: a 132 KB item took 7.5 s, 15.2 s and 83.4 s in current_line, and apply_answers with 200 lines took more than 600 s. Now each takes less than 0.01 s. The judgement output of this repository is identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
… review) The cache keys of assurance.of_model and evidence.of_model are the digest of the pickle of all inputs. A Meaning with a valid Mapping field that pickle cannot copy, such as types.MappingProxyType or a Mapping class in a trusted model file, stopped check, refresh and render with a TypeError. The base code accepted this input. The new function evidence.remembered() makes the key. If pickle cannot copy the inputs, it calculates the result and does not keep it. Both of_model functions use it. A new test gives a MappingProxyType and an unpicklable Mapping class, and compares the claims, the flow evidence and the SVG with the result for a dict. Both cases failed before this change. The module docstring of tests/test_render_cache.py now agrees with the test (three renders, one call each) and with the recorded 32.4 s. On the frozen copy at 136975f (3 runs each): check 6.00 s, refresh (current) 6.68 s, refresh (stale) 7.82 s. Router.route calls: 80. The output hashes are identical to the runs before and after the first cache change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
Give one error text for JSON that has too many levels. Split the namespace_packages docstring into short sentences. Change the test comment so that it states what the refresh test guards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
…ript file Review findings of the g1full-3, exec-3 and g1ts-4 change. The search for the error node in parse_problem uses a stack, not recursion. A deep TypeScript file with a syntax error now gives the cause `syntax` and the error line, not the cause `depth` with line 0. The surface reader uses pattern_names of the scope reader for the names of an exported binding pattern. Thus a deep exported pattern keeps its surface. The `depth` row of the reference and the DEPTH fix line now say that some facts of the file are not known. If a later step fails, the surface stays. Measured with `systemap extract` on a fixture with two files 3000 deep: before, the two modules had cause `depth` and line 0. After, the file with the error has cause `syntax` and line 1, and the pattern file is parsed with the name `a`. The facts of five normal fixtures are byte-identical before and after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
The coding agents page is built again from its source. The committed page agrees with the build, and the site test passes. The trust sentence in plugin.json, README.md and the coding agents page now gives a literal statement: if the command option runs code of the repository, the code of the repository can run when the live map starts. The map is refreshed. The flows CLI -> Scaffold, Config -> Scaffold and Scaffold -> Model cite the current digest of systemap.scaffold. The source reviews of Scaffold, ChangeDetector, Check, CLI, Config and Model were examined again and record the current digests. The claims did not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
schematic_script.py had a raw U+2028 and a raw U+2029 in _LINE_ENDS. Two test files had them also. The fact code counts lines with str.splitlines, so the loc of schematic_script was 477 and not 475, and each cited row after line 14 was two rows off. The files now use escapes, and a test makes sure that no source file or test file has such a character. The AnswerWriteError for text that UTF-8 cannot encode now says only that the command did not write the target file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
Refresh the map for the new module systemap.serve_guard. The CLI description now says that the page servers refuse a POST request from a page of a different web site. Record the current CLI source review digest. The trust and security guide page no longer says that the servers have no other access control. It gives the cross-site refusal, the read time limit and the maximum number of event connections. Rebuild docs/guide. The accept test closes the real socket before it puts the test socket in its place, so the test does not leave a socket open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
If the answer has no id, the sequence id is the entry point name, as before. Names such as 'GET /a' and 'pkg.reader:read' are not refused. An entry point name with a control character is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
The judgement command gives the current lines to answer_migrations.warnings. An item that is a current line gets no "Change it to" warning. Before this change, 4 exact current lines got a warning with a different line. The entry kind identifier pattern accepts a component name with a space. The name must be the same in the two positions. The map output is refreshed, so systemap check passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
# Conflicts: # docs/map/index.html # docs/map/map.json
# Conflicts: # docs/map/index.html # docs/map/map.json # map/model.py
# Conflicts: # src/systemap/failures.py # src/systemap/serve.py
# Conflicts: # docs/map/figures/control.svg # docs/map/figures/system.svg # docs/map/index.html # docs/map/map.json
Merge results of fix/p2-calls, fix/p2-render, fix/p2-facts, fix/p2-ci, fix/p2-server, fix/p2-textterm, fix/p2-textenc and fix/p2-migrations: - Refresh docs/map for the merged source. - Correct the source records of 51 flows whose cited modules changed (assurance, audit, check, cli, delta_report, evidence, extract, facts_file, jev_cli, journeys, judgement, page, route, schematic), and record their review digests after an examination of each flow claim. - Record the source reviews of 16 components after an examination of their claims. No claim text became false. - Add one CHANGELOG block for the P2 fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3Co8X9K3cT1u4rYQ9VwU
|
Architecture
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. |
This was referenced Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This change fixes the P2 findings of the audit. #128 had the P0 and P1 fixes.
Security
systemap liveandsystemap serverefuse a POST request ifOriginis a different origin ornull, or ifSec-Fetch-Siteiscross-site. Before this change, a web page in the browser could start a full render. A request without these headers (curl, Nodefetch) still works. The servers also have a timeout for the request read, a limit on the/eventsreaders, and a backoff after an accept error.@. Thus repository text cannot make a link, an image or a mention. The comment output of the workflow and of theinitworkflow template uses a random heredoc delimiter.systemaporuvx systemap, notuv run systemap.uv run systemapbuilds the project of the repository. The trust text is correct now.pre-commit run --all-files, with gitleaks, zizmor, actionlint and shellcheck.Robustness
depth. Before, it stopped the full extraction. The TypeScript walks use a stack, not recursion.judgement,check,renderandfactsexamine the shape of a storedmap.json. A malformed file gives an error that tells you to runsystemap refresh, not a traceback.refreshcan repair the file.decisions submitrefuses text that is not valid UTF-8 before it writes, so it writes nothing.answer_migrations.pyhas its fixed text at its fixed position, and each variable part has a limit. Each pattern has a negative test.Performance
systemap checkon this repository (scale-2)[judgement] answeredline (regex-3)The performance changes keep the same output. Each branch compared the map,
map.jsonand the figures of this repository before and after the change.Open items
These items stay open:
deltaandhistoryread stored facts without the shape check.package.jsonthat is nested 5000 deep can still giveRecursionErrorintypescript_config.package_json.jev_cli.cmd_auditcallsdecision_kinds.warningswithout the current lines.$.http.fetch(hooks/register.tsx) sendsOriginorSec-Fetch-Site. If it sends them, the refresh of the agent hook gets 403. Nodefetchsends neither, and the server accepts it.Checks
These checks pass on the head:
uv run pytest: 2311 passed, 7 skippeduv run pre-commit run --all-filesuv run mypy src --strictuv run systemap refreshuv run systemap checkuv run systemap judgement: no open findings, 16 current reviewsThe official ASD-STE100 dictionary was not available. A maintainer must examine the words of the new text.
Generated by Claude Code