Skip to content

Commit e7e10a4

Browse files
Merge pull request #441 from StepenkoAnatoli/fix/audit-p0
Independent audit P0 fixes: A-1, F01, F03, F02 and FP-011 (ADRs 0197-0199)
2 parents 532c696 + 5f44730 commit e7e10a4

33 files changed

Lines changed: 5642 additions & 136 deletions

‎CHANGELOG.md‎

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,110 @@ Each release of Fetchproof, newest first. The version is `KIT_VERSION` in
55

66
## Unreleased
77

8+
- **Fixed: a lost evidence table is named, and no check passes over it** (independent audit 2026-11-04, FP-011,
9+
alias B-2). `readCorpus` read a missing `research/EVIDENCE.md`, a directory in its place, or a file holding no
10+
evidence table as an empty table, with no problem recorded: `corpus-shape` said "the corpus parses cleanly",
11+
`citations` said "0 evidence rows, each with a cached page", the only failures were the citations that now
12+
dangled, and a corpus whose unknowns were all KNOWN-UNKNOWN passed. `citations` now returns one
13+
`evidence-missing` or `evidence-unparsed` fail and nothing else, and `corpus-shape` names the same beside
14+
`raw-missing`, in every gated project; the dangling citations are still named (ADR-0199). Four tests.
15+
- **Fixed: the browser route refuses a proxy it cannot speak to, instead of sending it the credential in
16+
clear** (independent audit 2026-11-04, F02). The guard proxy writes its requests and CONNECT lines to the
17+
upstream in plain HTTP, and `parseUpstream` read an `https://` proxy URL as such a proxy on port 443: behind
18+
`HTTPS_PROXY=https://user:pass@proxy:8443` the CONNECT line and its `Proxy-Authorization` header went in clear
19+
to a port that expects TLS. Any scheme but `http:` is now refused before the guard listens, so the render
20+
answers `GUARD` before a browser is launched and nothing reaches the proxy; the refusal never falls back to a
21+
direct connection, its message never contains the credential, and under `NO_PROXY=*` the upstream is not read
22+
(ADR-0198). A scheme-less `host:port` is still plain HTTP. Two tests.
23+
- **Fixed: a ZIP entry name another platform reads as a different name is refused** (independent audit
24+
2026-11-04, F03). The artifact reader judged a name by its spelling while an extractor writes it by its
25+
meaning: `project/research//BRIEF.md` validated PASS beside the reviewed brief and extracted onto it,
26+
`research/gate_off` and `research//GATE_OFF` carried the gate override past the never-packaged check, and a
27+
trailing dot, an NTFS stream, a device name or a case-folded file-folder pair each meant another file on
28+
Windows. `checkEntryName` now also refuses an empty segment, a character Windows does not allow, a segment
29+
ending in a dot or a space, a device name and an 8.3 short-name shape; duplicates, collisions and the
30+
never-packaged paths are compared by one key that folds case and Unicode form, and the never-packaged paths
31+
are matched as folders too. The producer refuses the same names before it writes (ADR-0197). Thirteen tests.
32+
- **Fixed: a table in an HTML comment or a code block is no longer read as the contract** (independent audit
33+
2026-11-04, F01; review of #441, 2026-11-06). The header search took the first table with the header wherever
34+
it stood, so an archived CLOSED copy of the unknowns table, commented out or kept as a fenced example above the
35+
live one, was read in its place: a visible OPEN unknown passed preflight and the portable state derived
36+
`buildAuthorized: true`. The header search, the stray-row scan and the heading above a table now share one
37+
reading of what GitHub shows as code or hides, `hiddenLines`, which follows cmark-gfm's block parser, the one
38+
GitHub renders with, line by line: fences, indented code, HTML blocks and comments - a comment reopened by a
39+
`<!--` after its `-->` - inside list items, block quotes and footnote definitions (ADR-0203). Read at first
40+
from trimmed lines, an indented example of a fence or a comment hid every
41+
table below it, and an archived copy after a list marker or in a reopened comment was still read. `appendRow`
42+
writes into the table the reader reads, and refuses (`TABLE_HIDDEN`), writing nothing, where its row would be
43+
hidden: below a fence or a comment never closed it started a new table inside the block, read by nothing, and
44+
numbered its row E-01 again on every run (ADR-0204). A collection, phase 0 and a dry run ask the same before
45+
anything is spent, and `collectOne` again before each fetch; `decompose.mjs` now answers the kit's named
46+
refusals with their message and exit 2, where it printed a stack under exit 1. The re-review of the fix found five
47+
more departures from GitHub's reading, each since checked on GitHub's renderer: with a byte-order mark before it,
48+
a fence or a comment opened on the first line was not seen; an empty list item stayed open across a blank line,
49+
and a heading's `===` or `--` was read as paragraph text, so a copy indented four below either was read; a list
50+
item's lazy continuation line ended the item, so a table indented in it, which GitHub shows, was read as code;
51+
and a line of 40,000 list markers took 18-23 s to read. Auto-collect now refuses a request into a project whose
52+
evidence or sources table is hidden before it reserves anything: it had charged the pages and paused the queue.
53+
The re-review of that fix (2026-11-07) found its own shortcuts reading six more shapes otherwise than GitHub, and
54+
the reading was ported to cmark-gfm's rules: an item it ended early let a comment or a fence below it be read as
55+
text, so an archived CLOSED copy there passed preflight again and `appendRow` wrote into it; a header and
56+
delimiter row written as lazy lines below an item, which GitHub makes no table of, were read as a table, and
57+
rows at column 0 below a table in an item as its rows; a footnote definition held nothing, and one nothing
58+
refers to, which GitHub leaves out, was read; a carriage return alone, a line end on GitHub, hid a fence's
59+
opener; two scans took time in the square of their input; and a comment cut off by its item, which leaves the
60+
rest of the page hidden, was taken as closed. A header GitHub makes no table of, and a row it shows apart from
61+
its table, are now named with why, the writer refuses in the same words, and a row is written at the
62+
indentation of the row it follows. The re-review of the port (2026-11-07) found the writer numbering past
63+
where the reader stops: in a table it found but stopped in above rows GitHub still shows, a new row went in
64+
above the stop with the next id there, a second U-2 beside one no check read. A write into a table the reader
65+
does not read whole is now refused - evidence, sources, and the contract a request is applied to - and a dry run
66+
of `decompose` leaves such a map, which it wrote over; `escapeCell` writes a carriage return alone, a line end
67+
on GitHub that split the sources table under a fetched title holding one, as a space. A table in a footnote
68+
definition, which GitHub shows below the body, is read only when the body holds none, and one in a definition
69+
GitHub drops for repeating a label not at all; a comment reopened after its `-->` is closed only by a `-->`
70+
GitHub writes as it stands; each line of nested footnote definitions is marked once, where they took 3.9 s
71+
on 200,000 lines; and a lazy header and lazy lines below a footnote definition are named in their own words.
72+
The re-review of that fix (2026-11-07) found comments read in the file's order, though GitHub shows the
73+
footnotes below the body: below a comment the body never closed, an archived CLOSED contract in a footnote
74+
passed preflight where GitHub shows no table or the live OPEN one, and a comment reopened in a footnote hid
75+
the body's table. A comment the body leaves open now hides the footnotes too, and one a footnote leaves open
76+
only what GitHub shows after it; a `[^label]` in a code span, a link or raw HTML no longer keeps a definition
77+
GitHub drops; nested definitions are each read in their own place; and a file ending inside a `<pre>` or a
78+
`<script>` never closed takes no table at its end. A table GitHub shows that no check reads - with no outer
79+
pipes, in a block quote, or ended by carriage returns alone - got a second table beside it, numbered from U-1
80+
or E-01 again; the writers now refuse beside it, naming the shape to fix. The re-review of that fix (2026-11-07)
81+
found such a table named only where no table was read: with an archived copy of its header further down, the
82+
copy was read as the contract, and preflight passed while the table GitHub shows first held an OPEN unknown. It
83+
is now a `table-split` problem wherever it stands before the table the checks read, and the writers refuse; one
84+
ended by carriage returns alone is named as a table no check can place, not as one GitHub shows. A `[^label]` is
85+
read by cmark-gfm's inline parse, with the link references the file defines: three rules had dropped references
86+
GitHub reads, so a definition it shows was left out and a later one's archived copy read in its place; one in a
87+
cell past the header's count, or followed by a `[ref]` the file defines, was counted; a bare URL or a `www.` link
88+
takes in a `[^label]` written straight after it; and a label is case-folded with its escapes and entities read,
89+
while one holding an entity name the kit does not know leaves its footnotes unjudged. The body and each footnote
90+
definition read their own comments and tags, so a definition's reopened comment is no longer dropped while the
91+
body's is open, and a comment or a tag left open in an HTML block of any type hides what follows. GitHub's page
92+
is one HTML stream, and what it writes between two HTML blocks can end a tag or a comment the kit keeps open: a
93+
table it may then show first, and every table read after an HTML block the browser may read from text, cannot
94+
be judged and are named so, where preflight passed on a CLOSED copy below the OPEN table GitHub shows, and the
95+
writers refuse beside them. The re-review of that fix (2026-11-08) found inline HTML opening what the kit never
96+
saw: GitHub writes a paragraph's, a heading's or a cell's raw HTML as it stands, and the browser ends a
97+
processing instruction or a CDATA section at a `>` inside it, and a comment at `--!>`, so a `<!--` or a quoted
98+
value after one hid the table below, and a `<select>` left open drops the tables after it: an archived CLOSED
99+
copy below either passed preflight again, and a request went into it. The tables after them, and the end of a
100+
file they leave in doubt, now cannot be judged, and the writers refuse there. The re-review of that fix found
101+
raw HTML over a line end read without it, where the browser takes a line end for white space: a quoted value
102+
or a comment opened on the next line still hid the table below, and a CLOSED copy was read; and the texts in
103+
doubt marked the lines in time quadratic in the file. Both are fixed. Three rules of the inline parse
104+
differed from cmark-gfm's: a link deactivated the brackets around it for good, where a later bracket opens them
105+
again; a label was case-folded by Node's Unicode, newer than GitHub's fold tables; and an inline link whose
106+
destination leaves a `(` open was taken for none. `upsertRow` of a new key reads the file once. Sixty-eight tests.
107+
- **Fixed: the commit gate blocks when it cannot make the folder it reads the index into** (independent audit
108+
2026-11-04, A-1). A `TEMP` that does not exist made `mkdtempSync` throw out of `materializeIndex`; the hook
109+
reported an internal error and, fail-open by default, allowed the commit, while a git failure in the same
110+
function blocked it. It is now an unreadable index (`scratch-failed`) and blocks, with a fix that names the temp
111+
folder. One test.
8112
- **Fixed: the gate reads the address a fetch asked for when that URL holds whitespace** (PR #440 review round 5,
9113
BR5-1 and its fix review, ADR-0193 decision 8). Every transport writes an argument holding whitespace
10114
JSON-quoted, and `requestedUrl` split the command on whitespace before it removed quotes, then tested the scheme

0 commit comments

Comments
 (0)