Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions .github/workflows/bandit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ on:
permissions:
contents: read

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
bandit-scan:
name: Bandit Security Scan
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/build-baseline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ on:
permissions:
contents: read

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
build-windows-native:
name: build / windows / amd64
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ on:
permissions:
contents: read

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
verify:
name: ci / build-and-test
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ permissions:
actions: read
contents: read

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
analyze:
name: codeql
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ on:
permissions:
contents: read

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
dependency-review:
name: dependency-review
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/ossf-scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ on:

permissions: read-all

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
analysis:
name: ossf-scorecard
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ on:
permissions:
contents: read

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
release-preflight:
name: release-preflight
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/sbom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ on:
permissions:
contents: read

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
supplemental-inventory:
name: supply-chain-inventory
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/secret-scan-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ on:
permissions:
contents: read

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
secret-scan:
name: secret-scan-gate
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/security-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ on:
permissions:
contents: read

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
audit:
name: security-audit
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/trivy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ on:
permissions:
contents: read

env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop

jobs:
trivy-fs-scan:
name: trivy-fs-scan
Expand Down
51 changes: 51 additions & 0 deletions scripts/checks/verify_supply_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@
"release artifact download must use skip-decompress: true and "
"repo-owned extraction before asset validation"
)
CHECKOUT_DEFAULT_BRANCH_GUARD_VIOLATION = (
"workflows using actions/checkout must set workflow-level "
"GIT_CONFIG_* init.defaultBranch env to avoid Git initial-branch warnings"
)
CHECKOUT_DEFAULT_BRANCH_GUARD_ENV = {
"GIT_CONFIG_COUNT": "1",
"GIT_CONFIG_KEY_0": "init.defaultBranch",
"GIT_CONFIG_VALUE_0": "develop",
}
OSSF_ARTIFACT_EXTRACTOR = "scripts/checks/extract_scorecard_artifact.py"
RELEASE_ARTIFACT_EXTRACTOR = "scripts/release/extract_release_artifacts.py"
OSSF_SARIF_NORMALIZER = "scripts/checks/normalize_scorecard_sarif.py"
Expand Down Expand Up @@ -389,6 +398,47 @@ def verify_pinned_actions() -> list[str]:
return violations


def workflow_top_level_env(content: str) -> dict[str, str]:
"""Return the simple top-level env mapping from a GitHub Actions workflow."""
env: dict[str, str] = {}
lines = content.splitlines()
for index, line in enumerate(lines):
if line != "env:":
continue
for env_line in lines[index + 1 :]:
if not env_line.strip() or env_line.lstrip().startswith("#"):
continue
if not env_line.startswith(" "):
break
match = re.match(r"^\s{2}([A-Za-z_][A-Za-z0-9_]*):\s*(.*?)\s*$", env_line)
if match is None:
continue
value = match.group(2).split(" #", 1)[0].strip().strip('"\'')
env[match.group(1)] = value
break
return env


def verify_checkout_default_branch_guard() -> list[str]:
"""Return checkout workflows missing the Git default-branch warning guard."""
violations: list[str] = []
workflow_paths = sorted(Path(".github/workflows").glob("*.yml")) + sorted(
Path(".github/workflows").glob("*.yaml")
)
for path in workflow_paths:
content = path.read_text(encoding="utf-8")
if "actions/checkout@" not in content:
continue
env = workflow_top_level_env(content)
if all(
env.get(key) == value
for key, value in CHECKOUT_DEFAULT_BRANCH_GUARD_ENV.items()
):
continue
violations.append(f"{path}: {CHECKOUT_DEFAULT_BRANCH_GUARD_VIOLATION}")
return violations
Comment thread
coderabbitai[bot] marked this conversation as resolved.


def verify_dependabot_coverage() -> list[str]:
"""Return missing Dependabot ecosystems from the repo configuration."""
path = Path(".github/dependabot.yml")
Expand Down Expand Up @@ -1546,6 +1596,7 @@ def main() -> int:
violations: list[str] = []
violations.extend(f"missing file: {item}" for item in verify_required_files())
violations.extend(verify_pinned_actions())
violations.extend(verify_checkout_default_branch_guard())
violations.extend(verify_dependabot_coverage())
violations.extend(verify_workflow_coverage())
violations.extend(verify_immutable_release_upload_policy())
Expand Down
165 changes: 165 additions & 0 deletions services/analysis-engine/tests/test_supply_chain_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,171 @@ def test_build_baseline_upload_artifact_pins_are_consistent() -> None:
assert len(set(pins)) == 1


def test_supply_chain_check_requires_checkout_default_branch_guard(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""Ensure checkout workflows suppress Git initial-branch warnings at source."""
supply_chain = load_module(
"scripts/checks/verify_supply_chain.py",
"verify_supply_chain_checkout_default_branch_guard",
)
workflow_dir = tmp_path / ".github" / "workflows"
workflow_dir.mkdir(parents=True)
(workflow_dir / "ci.yml").write_text(
"""
name: ci
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
""".strip(),
encoding="utf-8",
)

monkeypatch.chdir(tmp_path)

violations = supply_chain.verify_checkout_default_branch_guard()

assert violations == [
".github/workflows/ci.yml: workflows using actions/checkout must set "
"workflow-level GIT_CONFIG_* init.defaultBranch env to avoid Git "
"initial-branch warnings"
]


def test_supply_chain_check_rejects_commented_checkout_default_branch_guard(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""Ensure commented guard examples do not satisfy the checkout warning guard."""
supply_chain = load_module(
"scripts/checks/verify_supply_chain.py",
"verify_supply_chain_commented_checkout_default_branch_guard",
)
workflow_dir = tmp_path / ".github" / "workflows"
workflow_dir.mkdir(parents=True)
(workflow_dir / "ci.yml").write_text(
"""
name: ci
# env:
# GIT_CONFIG_COUNT: "1"
# GIT_CONFIG_KEY_0: init.defaultBranch
# GIT_CONFIG_VALUE_0: develop
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
""".strip(),
encoding="utf-8",
)

monkeypatch.chdir(tmp_path)

violations = supply_chain.verify_checkout_default_branch_guard()

assert violations == [
".github/workflows/ci.yml: workflows using actions/checkout must set "
"workflow-level GIT_CONFIG_* init.defaultBranch env to avoid Git "
"initial-branch warnings"
]


def test_supply_chain_check_rejects_run_step_checkout_default_branch_guard(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""Ensure later shell text does not satisfy the checkout warning guard."""
supply_chain = load_module(
"scripts/checks/verify_supply_chain.py",
"verify_supply_chain_run_step_checkout_default_branch_guard",
)
workflow_dir = tmp_path / ".github" / "workflows"
workflow_dir.mkdir(parents=True)
(workflow_dir / "ci.yml").write_text(
"""
name: ci
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- run: |
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop
""".strip(),
encoding="utf-8",
)

monkeypatch.chdir(tmp_path)

violations = supply_chain.verify_checkout_default_branch_guard()

assert violations == [
".github/workflows/ci.yml: workflows using actions/checkout must set "
"workflow-level GIT_CONFIG_* init.defaultBranch env to avoid Git "
"initial-branch warnings"
]


def test_supply_chain_check_rejects_nested_checkout_default_branch_guard(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""Ensure a single nested env block cannot satisfy the workflow guard."""
supply_chain = load_module(
"scripts/checks/verify_supply_chain.py",
"verify_supply_chain_nested_checkout_default_branch_guard",
)
workflow_dir = tmp_path / ".github" / "workflows"
workflow_dir.mkdir(parents=True)
(workflow_dir / "ci.yml").write_text(
"""
name: ci
jobs:
guarded:
runs-on: ubuntu-latest
env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: develop
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
unguarded:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
""".strip(),
encoding="utf-8",
)

monkeypatch.chdir(tmp_path)

violations = supply_chain.verify_checkout_default_branch_guard()

assert violations == [
".github/workflows/ci.yml: workflows using actions/checkout must set "
"workflow-level GIT_CONFIG_* init.defaultBranch env to avoid Git "
"initial-branch warnings"
]
Comment thread
coderabbitai[bot] marked this conversation as resolved.


def test_supply_chain_check_accepts_checkout_default_branch_guard(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Ensure checked-in checkout workflows carry the warning guard."""
supply_chain = load_module(
"scripts/checks/verify_supply_chain.py",
"verify_supply_chain_repo_checkout_default_branch_guard",
)
repo_root = Path(__file__).resolve().parents[3]

monkeypatch.chdir(repo_root)

violations = supply_chain.verify_checkout_default_branch_guard()

assert violations == []


def test_python_security_audit_does_not_ignore_patched_pygments_advisory() -> None:
"""Ensure patched Python advisories are not left as stale audit ignores."""
repo_root = Path(__file__).resolve().parents[3]
Expand Down
Loading