Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3849,7 +3849,10 @@ help_md = """Document how to verify release author.

[[controls."OSPS-DO-03.02".passes]]
handler = "pattern"
files = ["SECURITY.md", "README.md", "docs/releases.md", "CONTRIBUTING.md", "docs/RELEASE-VERIFICATION.md"]
# rst/txt/no-ext README variants added so this pass can PASS/FAIL
# deterministically on projects that don't ship .md-based READMEs
# (Python/Sphinx projects like tqdm surfaced the gap; see issue #402).
files = ["SECURITY.md", "README.md", "README.rst", "README.txt", "README", "docs/releases.md", "CONTRIBUTING.md", "docs/RELEASE-VERIFICATION.md"]

[controls."OSPS-DO-03.02".passes.pattern.patterns]
verify_author = "(verify|verif|signature|sign|GPG|Sigstore).*(author|release|identity)"
Expand Down Expand Up @@ -3905,7 +3908,8 @@ help_md = """Document support policy.

[[controls."OSPS-DO-04.01".passes]]
handler = "pattern"
files = ["SUPPORT.md", "SECURITY.md", "README.md"]
# rst/txt/no-ext README variants added (see #402 sibling change).
files = ["SUPPORT.md", "SECURITY.md", "README.md", "README.rst", "README.txt", "README", "docs/support.md"]

[controls."OSPS-DO-04.01".passes.pattern.patterns]
support_scope = "(support.*(scope|duration|period|timeline)|version.*(support|maintenance))"
Expand Down Expand Up @@ -3968,7 +3972,8 @@ help_md = """Document end-of-life policy.

[[controls."OSPS-DO-05.01".passes]]
handler = "pattern"
files = ["SUPPORT.md", "SECURITY.md", "README.md"]
# rst/txt/no-ext README variants added (see #402 sibling change).
files = ["SUPPORT.md", "SECURITY.md", "README.md", "README.rst", "README.txt", "README", "CONTRIBUTING.md"]

[controls."OSPS-DO-05.01".passes.pattern.patterns]
eol_policy = "(end.of.(life|support)|EOL|deprecat|sunset)"
Expand Down Expand Up @@ -4106,7 +4111,9 @@ look_for_urls = true

[[controls."OSPS-SA-01.01".passes]]
handler = "pattern"
files = ["ARCHITECTURE.md", "docs/architecture.md", "docs/design.md", "DESIGN.md", "README.md"]
# rst variants added; ARCHITECTURE/DESIGN commonly live as .rst
# in Sphinx-based Python projects.
files = ["ARCHITECTURE.md", "ARCHITECTURE.rst", "docs/architecture.md", "docs/architecture.rst", "docs/design.md", "docs/design.rst", "DESIGN.md", "DESIGN.rst", "README.md", "README.rst"]
pass_if_any = true

[controls."OSPS-SA-01.01".passes.pattern.patterns]
Expand Down Expand Up @@ -4179,7 +4186,9 @@ look_for_urls = true
# Check for API documentation in dedicated files OR README sections
[[controls."OSPS-SA-02.01".passes]]
handler = "pattern"
files = ["API.md", "docs/api.md", "README.md", "docs/README.md", "USAGE.md", "docs/getting-started.md", "openapi.yaml", "openapi.json", "swagger.yaml", "swagger.json"]
# README.rst added for Sphinx-based projects; API.rst / USAGE.rst
# variants common in the same ecosystem.
files = ["API.md", "API.rst", "docs/api.md", "docs/api.rst", "README.md", "README.rst", "docs/README.md", "USAGE.md", "USAGE.rst", "docs/getting-started.md", "openapi.yaml", "openapi.json", "swagger.yaml", "swagger.json"]
pass_if_any = true

[controls."OSPS-SA-02.01".passes.pattern.patterns]
Expand Down
95 changes: 95 additions & 0 deletions tests/darnit_baseline/controls/test_pattern_file_variants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""Regression guard for the pattern-handler file-list widening.

Sibling to `test_llm_eval_file_contents.py` (#402). The 5 controls
that go `pattern -> llm_eval` (no `file_exists` between) previously
listed .md-only README candidates in their `pattern.files`. On projects
that ship a .rst / .txt / no-extension README (tqdm, and any Sphinx-
based Python project), the pattern tier resolved INCONCLUSIVE and
llm_eval fired -- which was the trigger for #402's empty-file_contents
bug in the first place.

Widening the pattern lists to include README.rst / README.txt / README
means the deterministic tier PASSes or FAILs conclusively on those
repos, and the LLM tier fires less often. This test locks the widening
in place per control -- a revert of any entry re-opens the same class
of bug.
"""

from __future__ import annotations

from pathlib import Path

import pytest

try:
import tomllib
except ImportError: # Python 3.10
import tomli as tomllib # type: ignore

_FRAMEWORK_TOML = (
Path(__file__).resolve().parents[3]
/ "packages"
/ "darnit-baseline"
/ "src"
/ "darnit_baseline"
/ "openssf-baseline.toml"
)


def _load_framework() -> dict:
with open(_FRAMEWORK_TOML, "rb") as f:
return tomllib.load(f)


def _pattern_files(framework: dict, control_id: str) -> list[str]:
"""Return the `files` list of the first `pattern` pass on control_id."""
for p in framework["controls"][control_id]["passes"]:
if p.get("handler") in ("pattern", "regex"):
return list(p.get("files", []))
return []


# Controls whose pattern handler gates an llm_eval pass. Widening their
# file lists reduces how often the LLM tier fires on non-.md repos.
_GATED_CONTROLS = [
"OSPS-DO-03.02",
"OSPS-DO-04.01",
"OSPS-DO-05.01",
"OSPS-SA-01.01",
"OSPS-SA-02.01",
]


@pytest.fixture(scope="module")
def framework() -> dict:
return _load_framework()


@pytest.mark.parametrize("control_id", _GATED_CONTROLS)
def test_pattern_list_covers_rst_readme(framework: dict, control_id: str):
"""Each gated control's pattern.files MUST include README.rst.

Without it, the deterministic tier can't resolve on Sphinx-based
projects and the pipeline falls through to `llm_eval`.
"""
files = _pattern_files(framework, control_id)
assert "README.rst" in files, (
f"{control_id}: pattern handler's `files` list must include "
f"README.rst so the deterministic tier can resolve on rst-based "
f"projects (see #402). Current list: {files}"
)


@pytest.mark.parametrize(
"control_id",
["OSPS-DO-03.02", "OSPS-DO-04.01", "OSPS-DO-05.01"],
)
def test_pattern_list_covers_txt_and_no_ext_readme(
framework: dict, control_id: str
):
"""DO-* controls that scan README should also accept `README.txt`
and the no-extension `README` filename convention some older
projects still use."""
files = _pattern_files(framework, control_id)
assert "README.txt" in files, f"{control_id}: missing README.txt in {files}"
assert "README" in files, f"{control_id}: missing README in {files}"
Loading