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
31 changes: 31 additions & 0 deletions utils/tests/verify_action_build/test_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,37 @@ def test_cosign_verify_passes(self):
warnings, failures = analyze_binary_downloads("org", "repo", "a" * 40)
assert failures == []

def test_gh_attestation_verify_passes_and_prints_note(self, capsys):
# Mirrors the untitaker/hyperlink shape: action.yml runs an installer
# script that downloads from GitHub releases and verifies the download
# via `gh attestation verify`.
action_yml = """\
name: Test
runs:
using: composite
steps:
- name: Install
shell: bash
run: scripts/install.sh
"""
files = {
"scripts/install.sh": (
"#!/bin/sh\n"
"curl -fsSLO https://github.com/org/repo/releases/download/v1/installer.sh\n"
"gh attestation verify installer.sh --repo org/repo "
"--signer-workflow org/repo/.github/workflows/release.yml\n"
"sh installer.sh\n"
),
}
with mock.patch("verify_action_build.security.fetch_file_from_github", side_effect=self._mock_fetch(files)):
with mock.patch("verify_action_build.security.fetch_action_yml", return_value=action_yml):
warnings, failures = analyze_binary_downloads("org", "repo", "a" * 40)
assert failures == []
out = capsys.readouterr().err
assert "GitHub build attestation verification detected" in out
assert "Assumptions" in out
assert "fail-open" in out

def test_apk_add_not_flagged(self):
files = {
"Dockerfile": (
Expand Down
32 changes: 32 additions & 0 deletions utils/verify_action_build/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,36 @@ def _has_verification(content: str) -> bool:
return any(p.search(content) for p in _VERIFICATION_PATTERNS)


# Subset of the verification signal that specifically denotes GitHub build
# attestation verification (``gh attestation verify``). When a downloader
# script carries this we surface a dedicated, explicit note — it is the
# strongest of the verification mechanisms, but also one whose *enforcement*
# this static scan cannot prove (see the printed assumptions).
_ATTESTATION_VERIFY_PATTERN = re.compile(r"\bgh\s+attestation\s+verify\b")


def _has_attestation_verification(content: str) -> bool:
return bool(_ATTESTATION_VERIFY_PATTERN.search(content))


def _print_attestation_note(path: str) -> None:
"""Explain, in green, that attestation verification was detected and what
the static scan did (and did not) confirm by detecting it."""
console.print(
f" [green]✓ GitHub build attestation verification detected "
f"(`gh attestation verify`) in {path}[/green]"
)
console.print(
" [dim]Assumptions: this is a static text match. It confirms an "
"attestation-verify command is present in the same file as the "
"download; it does NOT run the installer, does NOT confirm the verify "
"call targets the artifact that gets downloaded, and does NOT detect "
"fail-open behaviour (e.g. verification skipped/warned when `gh` is "
"unavailable). Treat it as 'verification mechanism present', not "
"'verification proven to be enforced'.[/dim]"
)


def _action_yml_uses_external_verification(action_yml: str) -> bool:
return any(p.search(action_yml) for p in _VERIFICATION_USES_PATTERNS)

Expand Down Expand Up @@ -1521,6 +1551,8 @@ def analyze_binary_downloads(
console.print(
f" [green]✓[/green] {path}: {len(downloads)} download(s), {note}"
)
if _has_attestation_verification(content):
_print_attestation_note(path)
for line_num, snippet in downloads[:3]:
console.print(f" [dim]line {line_num}:[/dim] [dim]{snippet}[/dim]")
if len(downloads) > 3:
Expand Down