Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This folder contains the governance and maintenance documents that keep the publ
- [publication-workflow.md](publication-workflow.md): how to move from private/raw notes to public sanitized notes
- [taxonomy-closure.md](taxonomy-closure.md): canonical taxonomy state and future workflow
- [placeholder-closure.md](placeholder-closure.md): canonical placeholder state and checker workflow
- [repo-sentinel-baseline-review.md](repo-sentinel-baseline-review.md): redacted baseline classification and remote-gate decision record
- [maintenance-checkpoint.md](maintenance-checkpoint.md): current markdownlint maintenance mode, operator commands, and audit baseline
- [maintenance-quick-reference.md](maintenance-quick-reference.md): shortest-path maintainer commands by change type

Expand Down
82 changes: 82 additions & 0 deletions docs/repo-sentinel-baseline-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Repo-Sentinel Baseline Review

## Status

This is a classification record for the current consumer baseline. The
committed `.reposentinel-baseline.json` is unchanged, and no remote
`repo-sentinel` gate is enabled by this review.

The review keeps raw token values out of repository history, issues, and
reviewer-facing output.

## Audit Scope

The consumer snapshot is `sec-writeups-public` `main` at `9a18c74`. The
baseline is schema version `1`, generated at `2026-04-02T18:57:41Z`, and
contains 306 entries across 127 files.

The candidate audit used `repo-sentinel-lite` commit `8a6e064` from the
v0.8 development line. It is recorded as an immutable audit input, not as a
released dependency or a claim that the remote gate is ready.

Reproduction command:

```bash
repo-sentinel baseline audit \
--format json \
--baseline .reposentinel-baseline.json \
.
```

## Classification

| Audit class | Count | Classification | Decision |
| --- | ---: | --- | --- |
| Active `secret.high_entropy` | 274 | Reviewed documentation, path, and lab-example suppressions | Retain; do not regenerate automatically |
| Active `repo.required_file_missing` | 1 | Missing `LICENSE`; repository governance condition, not a secret false positive | Keep unresolved and do not call the baseline fully approved |
| Relocated | 5 | Existing README content moved to new lines | Retain the suppression; review the movement, do not rewrite automatically |
| Ambiguous | 26 | Duplicate path/link content in README and workflow files | Manually inspected as false positives; preserve audit visibility |
| Stale | 0 | No baseline entries disappeared in this audit | No removals required |
| Unmatched | 2,567 | Classified below by evidence type | Do not add to the committed baseline in this pass |

### Unmatched findings

| Evidence class | Count | Decision |
| --- | ---: | --- |
| Generated report artifacts under `reports/` | 2,414 | Fixture/artifact; keep as audit evidence and do not suppress through a bulk refresh |
| Assignment-context examples | 22 | Fixture; keep the educational command examples visible |
| Command placeholders | 19 | Fixture; preserve the teaching syntax and review through placeholder policy |
| Repository paths and documentation metadata | 81 | False positive; no credential claim is made from path-like text |
| Context-reviewed CI, documentation, and pattern-link metadata | 18 | False positive; repeated names and links are repository structure |
| Context-reviewed lab paths, flags, and example values | 13 | Fixture; values are challenge or lab examples, not production credentials |

The unmatched classification totals 2,567. No real credential is confirmed by
this review. A scan for common AWS, GitHub, OpenAI, Slack, private-key, JWT,
and long-hex marker formats found no matches; that heuristic does not replace
human review of future high-entropy findings.

## Governance Decision

The current baseline is useful as a reviewed suppression record, but it is not
ready to become a blocking remote gate yet.

1. Keep the existing baseline unchanged until the missing `LICENSE` decision is
resolved and the reviewed suppression boundary is explicit.
2. Keep baseline audit output non-blocking. The changed-file policy should fail
on new error findings while baseline drift remains an independent review
signal.
3. Consume a reviewed `repo-sentinel` release or pin a reviewed immutable
commit before enabling the remote job.
4. Add the synthetic pass/fail/redaction integration test in the consumer
workflow before making the check required.
5. Preserve the rollback path: remove the remote job while retaining the local
pre-push hook.

## Relationship To Issue #5

This record advances [issue #5](https://github.com/stacknil/sec-writeups-public/issues/5)
without claiming that the acceptance criteria are complete. The historical
issue snapshot and this v0.8 candidate audit are not directly comparable:
scanner rule coverage and baseline identity semantics changed between the two
runs. Future comparisons should always record the exact `repo-sentinel`
release or commit used for the audit.
3 changes: 3 additions & 0 deletions docs/reviewer-brief.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ security source-note repository with:

- Reproducible command: `python scripts/check_pattern_library.py`
- Deterministic outputs: pattern maturity, provenance counts, project links, case-study backlinks, rendered README snapshots, and generated tag docs.
- Provenance boundary: `last_reviewed` is compared with the UTC calendar date and tested with an injected clock.
- Tests / CI: pattern-contract validation, publication checks, placeholder checks, markdown checks, pre-commit hooks, and GitHub Actions workflows.
- Release evidence: stable pattern index, source-note links, governance docs, sanitization checklist, and maintenance checkpoints.
- Non-goals: raw exploit logs, private evidence dumps, live target identifiers, weaponized exploit chains, or unsanitized challenge transcripts.
Expand Down Expand Up @@ -72,3 +73,5 @@ notes independently reviewable.

Promote reviewed cards only when new evidence or implementation work increases
their decision value; avoid growing the source archive as an end in itself.
The next governance case is the reviewed `repo-sentinel` baseline; its audit
evidence is recorded separately before any remote gate becomes blocking.
7 changes: 5 additions & 2 deletions patterns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ These cards remain useful but are not part of the featured stable set:
| `stable` | Evidence-bounded, linked to a core implementation, and supported by at least one source note. |

Every card records `maturity` and `last_reviewed` in front matter. The review
date must be a valid current or historical date; future provenance claims fail
validation.
date is evaluated against the UTC calendar date
(`datetime.now(timezone.utc).date()`), so local timezone differences cannot
make a same-day review look like future provenance. Future provenance claims
fail validation, and the contract tests inject the clock rather than depending
on the machine's current date.

## Card Contract

Expand Down
16 changes: 13 additions & 3 deletions scripts/check_pattern_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import json
import re
import sys
from collections.abc import Callable
from dataclasses import dataclass
from datetime import date
from datetime import date, datetime, timezone
from pathlib import Path
from urllib.parse import unquote, urlparse

Expand All @@ -17,6 +18,7 @@
FRONT_MATTER_RE = re.compile(r"\A---\n(.*?)\n---\n", re.DOTALL)
HEADING_RE = re.compile(r"^## (.+)$", re.MULTILINE)
LINK_RE = re.compile(r"\[[^\]]+\]\(([^)]+)\)")
DateClock = Callable[[], date]


@dataclass(frozen=True)
Expand All @@ -35,11 +37,18 @@ def load_config() -> dict[str, object]:
return json.loads(CONFIG_PATH.read_text(encoding="utf-8"))


def utc_today() -> date:
"""Return the UTC calendar date used by local and CI provenance checks."""
return datetime.now(timezone.utc).date()


def parse_card(
path: Path,
required_sections: list[str],
maturity_values: set[str],
errors: list[str],
*,
clock: DateClock = utc_today,
) -> Card | None:
text = path.read_text(encoding="utf-8").replace("\r\n", "\n")
match = FRONT_MATTER_RE.match(text)
Expand All @@ -62,7 +71,7 @@ def parse_card(
except (TypeError, ValueError):
errors.append(f"{relative(path)}: last_reviewed must use YYYY-MM-DD")
return None
if reviewed > date.today():
if reviewed > clock():
errors.append(f"{relative(path)}: last_reviewed must not be in the future")
return None

Expand Down Expand Up @@ -112,7 +121,7 @@ def is_core_project_link(target: str, prefixes: list[str]) -> bool:
return any(target == prefix or target.startswith(f"{prefix}/") for prefix in prefixes)


def validate() -> tuple[list[str], int, int, int]:
def validate(*, clock: DateClock = utc_today) -> tuple[list[str], int, int, int]:
config = load_config()
required_sections = list(config["required_sections"])
maturity_values = set(config["maturity_values"])
Expand All @@ -135,6 +144,7 @@ def validate() -> tuple[list[str], int, int, int]:
required_sections,
maturity_values,
errors,
clock=clock,
)
)
is not None
Expand Down
32 changes: 30 additions & 2 deletions tests/test_validation_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import sys
import unittest
from datetime import date
from pathlib import Path
from tempfile import TemporaryDirectory
from unittest.mock import patch
Expand Down Expand Up @@ -56,15 +57,15 @@ def pattern_card_text(


class PatternLibraryContractTests(unittest.TestCase):
def test_parse_card_future_review_date_is_rejected(self) -> None:
def test_parse_card_future_review_date_is_rejected_against_utc_clock(self) -> None:
with TemporaryDirectory() as temp_dir:
temp_root = Path(temp_dir)
card_path = temp_root / "patterns" / "future-review.md"
card_path.parent.mkdir(parents=True)
card_path.write_text(
pattern_card_text(
title="Future review",
last_reviewed="2999-01-01",
last_reviewed="2026-07-16",
),
encoding="utf-8",
)
Expand All @@ -76,6 +77,7 @@ def test_parse_card_future_review_date_is_rejected(self) -> None:
REQUIRED_PATTERN_SECTIONS,
{"draft", "reviewed", "stable"},
errors,
clock=lambda: date(2026, 7, 15),
)

self.assertIsNone(card)
Expand All @@ -87,6 +89,32 @@ def test_parse_card_future_review_date_is_rejected(self) -> None:
],
)

def test_parse_card_review_date_on_utc_boundary_is_allowed(self) -> None:
with TemporaryDirectory() as temp_dir:
temp_root = Path(temp_dir)
card_path = temp_root / "patterns" / "boundary-review.md"
card_path.parent.mkdir(parents=True)
card_path.write_text(
pattern_card_text(
title="Boundary review",
last_reviewed="2026-07-16",
),
encoding="utf-8",
)
errors: list[str] = []

with patch.object(check_pattern_library, "ROOT", temp_root):
card = check_pattern_library.parse_card(
card_path,
REQUIRED_PATTERN_SECTIONS,
{"draft", "reviewed", "stable"},
errors,
clock=lambda: date(2026, 7, 16),
)

self.assertIsNotNone(card)
self.assertEqual(errors, [])

def test_validate_stable_card_without_core_project_is_rejected(self) -> None:
with TemporaryDirectory() as temp_dir:
temp_root = Path(temp_dir)
Expand Down
Loading