Dashboard › institutional-transition-lab › Session 0k60TJeo0Lqv
0k60TJeo0Lqvt0U3ZPerform a fresh read-only security audit of only input descriptor opening and bounded reads for issue #4 in /home/byk/Code/institutional-transition-lab. This replaces a reviewer that returned empty twice. Never edit files and never inspect governance bodies, coding decisions, outcomes, reports, dates, detector output, or source-adjudication scopes.
Scope only: _read_regular_file and directly related constants/tests; rejection of symlinks, FIFOs, sockets, block/character devices, directories, and other special files without blocking or following links; descriptor/path TOCTOU; exact byte-limit enforcement, short/partial reads, descriptor cleanup, and error behavior. Do not review JSON parsing, pins/hashes, ZIP internals, or CLI output writing.
Review current working copy. Run narrow read-only tests/probes. Return substantive evidence, never empty. Findings first, severity ordered, exact current file:line references, PASS/CONCERN/MUST-FIX/BLOCKED labels. Every defect needs a deterministic regression. If clean, state PASS with tests/probes and residual risks. End exactly MERGE or DO-NOT-MERGE.
Found 26 matches
/home/byk/Code/institutional-transition-lab/tests/test_governance_adjudication.py:
Line 940: "from institution_lab.governance_adjudication import _read_regular_file; "
Line 941: f"_read_regular_file(Path({str(fifo)!r}))"
/home/byk/Code/institutional-transition-lab/src/institution_lab/governance_adjudication.py:
Line 65: MAX_INPUT_BYTES = 16 * 1024 * 1024
Line 66: MAX_ARTIFACT_BYTES = 32 * 1024 * 1024
Line 68: MAX_ARCHIVE_MEMBER_BYTES = 16 * 1024 * 1024
Line 69: MAX_ARCHIVE_EXPANDED_BYTES = 64 * 1024 * 1024
Line 94: def _read_regular_file(path: Path, *, max_bytes: int = MAX_INPUT_BYTES) -> bytes:
Line 113: return _bytes_sha256(_read_regular_file(path, max_bytes=MAX_ARTIFACT_BYTES))
Line 188: if sum(info.file_size for info in infos) > MAX_ARCHIVE_EXPANDED_BYTES:
Line 199: if info.file_size > MAX_ARCHIVE_MEMBER_BYTES:
Line 202: member_bytes = source.read(MAX_ARCHIVE_MEMBER_BYTES + 1)
Line 203: if ...
Found 8 matches
/home/byk/Code/institutional-transition-lab/tests/test_governance_adjudication.py:
Line 813: @pytest.mark.parametrize("alias_kind", ["direct", "symlink", "hardlink"])
Line 825: elif alias_kind == "symlink":
Line 826: paths["summary_json"].symlink_to(paths[input_name])
Line 931: def test_regular_file_reader_rejects_fifo_without_blocking(tmp_path: Path) -> None:
Line 932: fifo = tmp_path / "input.fifo"
Line 933: os.mkfifo(fifo)
Line 941: f"_read_regular_file(Path({str(fifo)!r}))"
Line 951: assert "input path is not a regular file" in result.stderr
<path>/home/byk/Code/institutional-transition-lab/src/institution_lab/governance_adjudication.py</path>
<type>file</type>
<content>
45: "documents_sha256": "d70f7f795847968339a645b6dbd6d4428af6bd6044de44fa2602d4c4343b0ee9",
46: "documents_file_sha256": "2a62ab05687982c112a29f6126227c26c3ff8defc89ca64662753aaf85c3c0ce",
47: "coding_package_file_sha256": (
48: "14644759f53071adfe1173d4e25bb01931b8c1591fa8a48295dbd0556a754769"
49: ),
50: "protocol_sha256": "06d4b3407677fe7fb23046f70164a366db06638e8a29af3daf7661f00d565933",
51: "coding_schema_sha256": "bfc735a7d8602d1f236a88c2d23b195f0600a8bae86fb8c8936afe7883a41b8e",
52: "adjudication_schema_sha256": (
53: "6181f5cb60a0462f83be00be301763daa89f11581e68ee1c46a84e8bf4e5be31"
54: ),
55: }
56: CLAIM_FIELDS = {
57: "record_class",
58: "event_kind",
59: "title",
60: "announced_on",
61: "effective_on",
62: "affected_scope",
63: "body_patch_relation",
64: }
65: MAX_INPUT_BYTES = 16 * 1024 * 1024
66: MAX_ARTIFACT_BYTES = 32 * ...
<path>/home/byk/Code/institutional-transition-lab/src/institution_lab/governance_adjudication.py</path>
<type>file</type>
<content>
1: from __future__ import annotations
2:
3: import argparse
4: import hashlib
5: import io
6: import json
7: import os
8: import stat
9: import tempfile
10: import zipfile
11: from collections import Counter
12: from contextlib import suppress
13: from dataclasses import dataclass
14: from pathlib import Path
15: from typing import Any
16:
17: from jsonschema import Draft202012Validator, FormatChecker
18:
19: from institution_lab.governance_coding import validate_response
20:
21: DECISIONS = {"accept", "revise", "reject", "abstain"}
22: INSTITUTIONAL_CLASSES = {
23: "effective_institutional_change",
24: "announced_institutional_change",
25: "proposal_only",
26: }
27: REVIEWER_TYPES = {"human", "llm", "human_llm_assisted"}
(Showing lines 1-27 of 946. Use offset=28 to continue.)
</content>
<path>/home/byk/Code/institutional-transition-lab/tests/test_governance_adjudication.py</path>
<type>file</type>
<content>
910: monkeypatch: pytest.MonkeyPatch,
911: ) -> None:
912: manifest, paths = _write_pinned_cli_inputs(tmp_path, monkeypatch)
913: with zipfile.ZipFile(paths["artifact"], "a") as archive:
914: for index in range(MAX_TEST_ARCHIVE_MEMBERS):
915: archive.writestr(f"padding/{index}", b"")
916: manifest["artifact_sha256"] = file_sha256(paths["artifact"])
917: monkeypatch.setattr(governance_adjudication, "FROZEN_INPUT_PINS", dict(manifest))
918:
919: with pytest.raises(ValueError, match="artifact member count exceeds limit"):
920: verify_input_manifest(
921: manifest,
922: artifact=paths["artifact"],
923: documents=paths["documents"],
924: coding_package=paths["coding_package"],
925: protocol=paths["protocol"],
926: coding_schema=paths["coding_schema"],
927: adjudication_schema=paths["adjudication_schema"],
928: )
929:
930:
931: def ...
<path>/home/byk/Code/institutional-transition-lab</path> <type>directory</type> <entries> .git/ .github/ .gitignore .lore.md .pytest_cache/ .ruff_cache/ .venv/ AGENTS.md cases/ data/ docs/ node_modules/ package-lock.json package.json PROTOCOL.md pyproject.toml README.md research/ schema/ scripts/ src/ tests/ uv.lock (23 entries) </entries>
Found 1 matches /home/byk/Code/institutional-transition-lab/tests/test_governance_adjudication.py: Line 951: assert "input path is not a regular file" in result.stderr
I’ll inspect only the reader, its limits, and its direct test, then run cache-disabled probes for file types, races, boundaries, and descriptor cleanup. I will not open any governance artifacts or derived research data.
. [100%] 1 passed in 0.38s
PASS reject final symlink: cannot open input as a regular file: /tmp/regular-reader-audit-4w4av91l/final-symlink
Traceback (most recent call last):
File "<stdin>", line 29, in <module>
File "<stdin>", line 14, in reject
File "/home/byk/Code/institutional-transition-lab/src/institution_lab/governance_adjudication.py", line 100, in _read_regular_file
with os.fdopen(descriptor, "rb") as source:
~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "<frozen os>", line 1069, in fdopen
IsADirectoryError: [Errno 21] Is a directory: 4
The first probe found a real fail-closed gap before reaching the remaining cases: opening a directory reaches os.fdopen first, which raises IsADirectoryError and leaves the raw descriptor unowned. I’m rerunning the disjoint cases with fault capture so one rejection cannot stop the audit.
PASS reject final symlink with ValueError: cannot open input as a regular file: /tmp/regular-reader-audit-owz72hfq/final-symlink PASS reject missing path with ValueError: cannot open input as a regular file: /tmp/regular-reader-audit-owz72hfq/missing PASS reject FIFO with ValueError: input path is not a regular file: /tmp/regular-reader-audit-owz72hfq/fifo PASS reject Unix socket with ValueError: cannot open input as a regular file: /tmp/regular-reader-audit-owz72hfq/socket PASS reject character device with ValueError: input path is not a regular file: /dev/null PASS reject limit + 1 regular file with ValueError: input file exceeds 7 bytes: /tmp/regular-reader-audit-owz72hfq/boundary PASS exact byte boundary: 7 accepted, 8 rejected PASS reject file grown after fstat with ValueError: input file exceeds 7 bytes: /tmp/regular-reader-audit-owz72hfq/grow PASS post-fstat growth is caught by the max+1 read PASS path replacement after open reads the already-opened inode OBSERVED ancestor ...
OBSERVED final symlink is followed when O_NOFOLLOW is unavailable
PASS successful read: descriptor closed PASS oversize rejection: descriptor closed PASS read error: descriptor closed
?? src/institution_lab/governance_adjudication.py ?? tests/test_governance_adjudication.py
MUST-FIX — directory and fdopen failures leak descriptors
os.open() creates the descriptor at src/institution_lab/governance_adjudication.py:97.os.fdopen() succeeds at line 100. If it raises, no finally closes the raw descriptor.IsADirectoryError at line 100 before the intended type check at lines 101–103. The descriptor remained open.fdopen(...)=ENOMEM failure produced the same leak.ValueError, then assert the captured descriptor returns EBADF.os.fdopen raise, assert the raw descriptor is closed.MUST-FIX — symlink rejection remains fail-open
O_NOFOLLOW at line 95 protects only the final path component.getattr(os, "O_NOFOLLOW", 0) at line 95 silently disables protection when the flag is unavailable. Removing the attribute in a probe caused a final symlink to be followed and read.O_NOFOLLOW is unavailable, the helper must fail before opening the path.CONCERN — arbitrary device opening is not guaranteed nonblocking
fstat() classifies it at lines 101–103.O_NONBLOCK at line 95 prevented the tested FIFO from blocking, but device drivers do not universally guarantee nonblocking or side-effect-free open()./dev/null and synthetic block-device metadata were rejected. I did not open a live block device. ...Date: Sep 9, 2026 * 🔴 [enforced-read-only] (04:36) User directed that files must never be edited and governance bodies must never be inspected. * 🔴 (04:36) User prohibited inspection of coding decisions, outcomes, reports, dates, detector output, and source-adjudication scopes. * 🟡 [requested-security-audit] (04:36) User requested a fresh, read-only security audit for issue #4 in `/home/byk/Co…