Skip to content

Commit ea99fac

Browse files
committed
test(security): cover rejected package execution
1 parent abf71a0 commit ea99fac

4 files changed

Lines changed: 318 additions & 66 deletions

File tree

Lines changed: 69 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,75 @@
1-
# TDD evidence: workspace root-command registration
1+
# TDD evidence: workspace registration isolation
22

3-
Date: 2026-10-11 (Europe/Berlin). Production baseline: dev `4e7c38c2830f4bc53ffe78eeb0a2bfbabfc711e2`.
4-
Environment: macOS, CPython 3.13.14, dedicated frozen uv environment.
3+
Date: 2026-10-11 (Europe/Berlin). Protected baseline: dev
4+
`4e7c38c2830f4bc53ffe78eeb0a2bfbabfc711e2`. Local environment: macOS, CPython 3.13.14,
5+
dedicated frozen uv environment. The published PR remains unmerged.
56

6-
## Failing-before
7+
## Retained first implementation history
78

8-
Specification amended before test authoring, including the existing module-grouping requirement and permanent
9-
core-command authority. Command: `.venv/bin/pytest -q -o addopts='' tests/unit/security/test_module_command_squatting.py`.
10-
Initial result: four behavioral failures, five passing compatibility controls. The missing-category root is
11-
registered, all three permanent core roots are extended, and an escaping symlink gains authority in both modes.
12-
No production code changed. No skips; native hosted authored execution is pending publication.
9+
Historical authored head `8a03cf5898335f22dc411d75e22ea74d700df0fa` and native RED
10+
[run 38172950463](https://github.com/nold-ai/specfact-cli/actions/runs/38172950463) preceded the first
11+
implementation `facb6904843ab38521e8129024fddfa25b38247e`. The original digest was explicitly accepted
12+
by djm81. That implementation passed 78 focused tests and a fresh full run: 3,298 passed, 11 disclosed
13+
skips, two dependency deprecation warnings, 328.73 seconds. An unchanged native rerun after stack
14+
registration passed producer, independent execution and final consumer. This historical evidence does
15+
not cover the amended mapping or test file and is not reused as current proof.
16+
17+
## Review corrections and valid failing-before evidence
18+
19+
Added specification deltas first for official category roots, rejected-package import isolation,
20+
effective operator-selected built-ins and reconciliation of shipped categorized routing in disabled mode.
21+
The first review test invocation had a collection syntax error; subsequent draft category fixtures lacked
22+
required grouping fields and failed manifest validation before registration. Both are discarded as proof.
23+
24+
Corrected all six category manifests to their valid canonical bundle/group fields and repeated the probes
25+
before final production edits. Command: `.venv/bin/pytest -q -o addopts=''`
26+
`tests/unit/security/test_module_command_squatting.py`.
27+
28+
- Against historical implementation facb690: 14 actual assertion/behavior failures, nine passes,
29+
zero skips, 1.79 seconds. Twelve category-root cases, import-shadow execution and effective-root
30+
compatibility reproduce the remaining gaps.
31+
- Against protected baseline 4e7c38c, with the existing disabled-grouping bootstrap control also selected:
32+
17 actual assertion/behavior failures, seven passes, zero skips, 1.36 seconds. This includes the
33+
original missing-category and permanent-core/symlink vulnerabilities.
34+
35+
Signed proposal/test history is rebuilt from protected dev so no production change precedes the current
36+
native authored boundary. The earlier authored replay 9fef6a3f contains draft fixtures and is superseded;
37+
its hosted run is not accepted as current proof. Corrected tests must stay byte-identical after native RED.
38+
39+
The amended mapping digest is
40+
`sha256:a9ba0dfde727a2a82f037212ebe5c6eeacd2574602e7d6ca9e4e3900cbdeb95c`.
41+
The human owner explicitly approved recording acceptance as djm81 after confirmation that all 14 remaining
42+
behavior failures will be fixed. The old acceptance is not carried across a changed mapping digest.
1343

1444
## Passing-after
1545

16-
Command: `.venv/bin/pytest -q -o addopts='' tests/unit/security/test_module_command_squatting.py
17-
tests/unit/specfact_cli/registry/test_module_packages.py tests/unit/registry/test_category_groups.py
18-
tests/integration/test_category_group_routing.py`. Result: 78 passed, zero skips, 6.80 seconds.
19-
All four reproduced failures now pass; explicit legacy, non-project sources, categorized routes and built-in
20-
loading remain functional. Tests and mapping are unchanged from the signed authored boundary.
21-
22-
The first replay contained an unsigned planning ancestor. It was rejected during signature readback and the
23-
whole authored history was rebuilt with signatures; the new authored head is
24-
`8a03cf5898335f22dc411d75e22ea74d700df0fa`, tree `fc791df54e922e4a41f73161b5e878a8519acc5a`.
25-
Earlier head/run identities are superseded; hosted RED and final validation must bind to the new ancestry.
26-
27-
## Local independent gates
28-
29-
Frozen delivery and uv lock checks, all three security audits, dependency-trust register, license gate and
30-
strict module signatures pass. No dependency versions changed; only project version metadata changed.
31-
Strict PyPI-ahead: 0.55.7 > 0.55.4; all four version sources agree. BasedPyright: zero errors, the same
32-
1,522 baseline warnings; Ruff passes, Bandit blocking findings are absent, Semgrep has zero current/baseline
33-
findings. Final signed publication and current-head hosted execution remain pending.
34-
35-
## Hosted authored RED
36-
37-
[Native run 38172950463](https://github.com/nold-ai/specfact-cli/actions/runs/38172950463) binds to signed
38-
head `8a03cf5898335f22dc411d75e22ea74d700df0fa`. Its authored plan passes at test-authored maturity;
39-
the RED report passes at red maturity with no reconciliation findings, retaining two actual failing security
40-
selectors. The producer job is intentionally failed because the tests reproduce the vulnerable baseline.
41-
Final implementation must preserve those tests, mapping and authored ancestor and pass its independent consumer.
42-
43-
## Review and full-suite corrections
44-
45-
The first full run found two stale test assumptions: an external tmp_path labeled builtin was expected to
46-
register permanent core roots, and the version assertion still expected 0.55.6. The bootstrap control now
47-
uses the actual bound built-in root; the version assertion retains equality across all sources at 0.55.7.
48-
No security guard or assertion was disabled. The corrected bootstrap/version/security selection passes
49-
35 tests. The full fresh rerun passes: `hatch run smart-test-force --level full`, 3,298 passed, 11 reported skips, two dependency deprecation warnings, 328.73 seconds on local CPython 3.13.14. No security control is skipped. Explicit skip reasons are retained in local JUnit; hosted Python 3.11–3.13 and required checks remain independent.
50-
51-
Changed-Python review covers module_packages.py, the new security controls, both affected existing test
52-
files, setup.py and both version __init__ files. Independent merged-baseline review and candidate review each
53-
contain five warnings and five information findings, zero errors and zero new findings. New security tests
54-
are clean. Narrow baseline exception: existing importer/public-discovery naming flags, typed dependency
55-
parser duplication, deliberate exception propagation/None-preserving branches, and unrelated release-fixture
56-
style suggestions are unchanged outside the registration fix. Renaming public discovery APIs or refactoring
57-
those parser/test boundaries would widen this security patch. Their existing advice remains recorded;
58-
no analyzer rule, threshold, suppression, evidence source or required gate was weakened.
59-
60-
## Review regression boundary (2026-10-11)
61-
62-
Before production edits, added scenarios and selected executable cases for official category root claims,
63-
rejected-package import shadowing, effective operator-selected discovery roots and existing categorized
64-
routing in disabled grouping mode. The first test invocation had a collection syntax error and provides
65-
no behavior evidence; corrected collection then produced 14 actual failures, nine passes, zero skips in
66-
2.74 seconds on the unchanged implementation head facb6904843ab38521e8129024fddfa25b38247e.
67-
The twelve category-root cases, import-shadow execution and effective-root compatibility fail as expected.
68-
The approved mapping 903945b4 is historical; the amended mapping requires fresh human acceptance before
69-
native authored/final claims. No prior acceptance is carried across a changed digest.
46+
The corrected security and registry/category-routing/bootstrap selection passes 99 tests, zero skips,
47+
two existing dependency warnings, 7.58 seconds. Rejected packages are excluded before schema, bridge,
48+
protocol, active-import and category-mount side effects. All six official category root names are denied
49+
to project packages in both modes. Core command authority uses effective operator-selected discovery;
50+
other built-in integrity exemptions remain bound to the running package tree. Removed flat bundle shims
51+
are not restored. Native current-head RED/final and a fresh full suite remain pending.
52+
53+
## Independent gates and limits
54+
55+
The first implementation passed frozen delivery/uv lock, all three security audits, trust register,
56+
license gate and strict module signatures; no dependency payload changed. All four project version
57+
sources agree at 0.55.7. Strict PyPI-ahead was 0.55.7 > 0.55.4. BasedPyright reported zero errors and
58+
1,522 baseline warnings; Ruff, Semgrep and blocking Bandit findings were clean. The expanded candidate
59+
must rerun applicable gates. No skip, threshold, signature check, fixture pin or acceptance requirement is
60+
weakened. A new test type warning was fixed through a typed monkeypatch path replacement, without suppression.
61+
62+
First-implementation broad review and independent merged-baseline review each contained five warnings,
63+
five information findings and zero errors, with zero new findings. Narrow unchanged-baseline exceptions
64+
covered public discovery naming, typed parser duplication, deliberate propagation/None branches and unrelated
65+
release-fixture styles. Expanded code review must verify that no new findings remain; current tests are clean.
66+
67+
## Exact atomic selector correction
68+
69+
The released provider requires each selected RED node to fail and exact non-parameterized node identities.
70+
Run 38175451297 is discarded because positive-only controls passed in RED and parameter expansions did not
71+
match the declared node IDs. The accepted mapping digest and selector names remain unchanged. Each exact
72+
case now checks a rejected workspace/external boundary together with its supported counterpart; the same
73+
assertions run in RED and final, without stage flags or artificial failure. Parameter combinations execute
74+
inside the selected case, with isolated registries and monkeypatch contexts. Full independent tests also
75+
retain all six root names and both modes. Corrected native proof and final-head gates remain pending.
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{
22
"schema_version": "1",
3-
"decision": "pending",
3+
"decision": "accepted",
4+
"reviewer_id": "djm81",
5+
"reviewer_role": "repository-owner",
6+
"recorded_at": "2026-10-11T21:05:54.512555+00:00",
47
"reference": "https://github.com/nold-ai/specfact-cli/pull/719",
58
"mapping_digest": "sha256:a9ba0dfde727a2a82f037212ebe5c6eeacd2574602e7d6ca9e4e3900cbdeb95c"
69
}

‎tests/unit/registry/test_category_groups.py‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,30 @@ def test_bootstrap_with_category_grouping_enabled_registers_group_commands() ->
5454
)
5555

5656

57-
def test_bootstrap_with_category_grouping_disabled_still_has_no_flat_shims() -> None:
58-
"""Flat bundle shims are not registered even when SPECFACT_CATEGORY_GROUPING_ENABLED is false."""
57+
def test_bootstrap_with_category_grouping_disabled_still_has_no_flat_shims(
58+
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
59+
) -> None:
60+
"""Disabled grouping preserves category policy and cannot install a workspace category root."""
61+
from specfact_cli.registry import module_packages
62+
63+
builtin_packages = module_packages.discover_package_metadata(module_packages.get_modules_root(), source="builtin")
64+
workspace_root = tmp_path / ".specfact" / "modules"
65+
hostile = workspace_root / "hostile"
66+
(hostile / "src").mkdir(parents=True)
67+
(hostile / "module-package.yaml").write_text(
68+
"name: hostile\nversion: '0.1.0'\ncommands: [project]\ncategory: project\n"
69+
"bundle: specfact-project\nbundle_group_command: project\nbundle_sub_command: project\n"
70+
)
71+
(hostile / "src" / "app.py").write_text("import typer\napp = typer.Typer()\n")
72+
project_packages = module_packages.discover_package_metadata(workspace_root, source="project")
73+
monkeypatch.setattr(module_packages, "discover_all_package_metadata", lambda: builtin_packages + project_packages)
74+
monkeypatch.setattr(module_packages, "read_modules_state", dict)
5975
with patch.dict(os.environ, {"SPECFACT_CATEGORY_GROUPING_ENABLED": "false"}, clear=False):
6076
register_builtin_commands()
6177
rebuild_root_app_from_registry()
6278
names = [name for name, _ in CommandRegistry.list_commands_for_help()]
79+
assert "project" not in names, "Workspace native category roots remain reserved in disabled grouping"
80+
assert {"init", "module", "upgrade"}.issubset(names)
6381
forbidden_flat = {
6482
"analyze",
6583
"drift",

0 commit comments

Comments
 (0)