Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .github/workflows/sast-semgrep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ jobs:
*) code=true ;;
esac
case "$changed_path" in
requirements*.txt|*/requirements*.txt|pyproject.toml|*/pyproject.toml|uv.lock|*/uv.lock|pylock.*.toml|*/pylock.*.toml|package.json|*/package.json|package-lock.json|*/package-lock.json|pnpm-lock.yaml|*/pnpm-lock.yaml|yarn.lock|*/yarn.lock|Cargo.toml|*/Cargo.toml|Cargo.lock|*/Cargo.lock|go.mod|*/go.mod|go.sum|*/go.sum|pom.xml|*/pom.xml|build.gradle|*/build.gradle|build.gradle.kts|*/build.gradle.kts|DESCRIPTION|*/DESCRIPTION) deps=true ;;
requirements*.txt|*/requirements*.txt|pyproject.toml|*/pyproject.toml|uv.lock|*/uv.lock|pylock.*.toml|*/pylock.*.toml|package.json|*/package.json|package-lock.json|*/package-lock.json|pnpm-lock.yaml|*/pnpm-lock.yaml|yarn.lock|*/yarn.lock|Cargo.toml|*/Cargo.toml|Cargo.lock|*/Cargo.lock|go.mod|*/go.mod|go.sum|*/go.sum|pom.xml|*/pom.xml|build.gradle|*/build.gradle|build.gradle.kts|*/build.gradle.kts|DESCRIPTION|*/DESCRIPTION|poetry.lock|*/poetry.lock|pdm.lock|*/pdm.lock|Pipfile|*/Pipfile|Pipfile.lock|*/Pipfile.lock|setup.py|*/setup.py|setup.cfg|*/setup.cfg|environment.yml|*/environment.yml|environment.yaml|*/environment.yaml|bun.lock|*/bun.lock|bun.lockb|*/bun.lockb|deno.lock|*/deno.lock|Gemfile|*/Gemfile|Gemfile.lock|*/Gemfile.lock|gems.rb|*/gems.rb|gems.locked|*/gems.locked|composer.json|*/composer.json|composer.lock|*/composer.lock|conanfile.txt|*/conanfile.txt|conanfile.py|*/conanfile.py|conan.lock|*/conan.lock|vcpkg.json|*/vcpkg.json|packages.config|*/packages.config|packages.lock.json|*/packages.lock.json|deps.json|*/deps.json|Directory.Packages.props|*/Directory.Packages.props|settings.gradle|*/settings.gradle|settings.gradle.kts|*/settings.gradle.kts|gradle.lockfile|*/gradle.lockfile|buildscript-gradle.lockfile|*/buildscript-gradle.lockfile|libs.versions.toml|*/libs.versions.toml|verification-metadata.xml|*/verification-metadata.xml|mix.exs|*/mix.exs|mix.lock|*/mix.lock|pubspec.yaml|*/pubspec.yaml|pubspec.lock|*/pubspec.lock|Package.swift|*/Package.swift|Package.resolved|*/Package.resolved|Podfile|*/Podfile|Podfile.lock|*/Podfile.lock|Cartfile.resolved|*/Cartfile.resolved|renv.lock|*/renv.lock|stack.yaml.lock|*/stack.yaml.lock|flake.lock|*/flake.lock|go.work|*/go.work|go.work.sum|*/go.work.sum|deno.json|*/deno.json|deno.jsonc|*/deno.jsonc|MODULE.bazel|*/MODULE.bazel|MODULE.bazel.lock|*/MODULE.bazel.lock|WORKSPACE|*/WORKSPACE|WORKSPACE.bazel|*/WORKSPACE.bazel|maven_install.json|*/maven_install.json|Manifest.toml|*/Manifest.toml|Project.toml|*/Project.toml|.terraform.lock.hcl|*/.terraform.lock.hcl|*.MODULE.bazel|*.tf|*.tofu|*.csproj|*.fsproj|*.vbproj|*.gemspec|*.nuspec) deps=true ;;
esac
done <<<"$changed"
else
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/security-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ jobs:
*) code=true ;;
esac
case "$changed_path" in
requirements*.txt|*/requirements*.txt|pyproject.toml|*/pyproject.toml|uv.lock|*/uv.lock|pylock.*.toml|*/pylock.*.toml|package.json|*/package.json|package-lock.json|*/package-lock.json|pnpm-lock.yaml|*/pnpm-lock.yaml|yarn.lock|*/yarn.lock|Cargo.toml|*/Cargo.toml|Cargo.lock|*/Cargo.lock|go.mod|*/go.mod|go.sum|*/go.sum|pom.xml|*/pom.xml|build.gradle|*/build.gradle|build.gradle.kts|*/build.gradle.kts|DESCRIPTION|*/DESCRIPTION) deps=true ;;
requirements*.txt|*/requirements*.txt|pyproject.toml|*/pyproject.toml|uv.lock|*/uv.lock|pylock.*.toml|*/pylock.*.toml|package.json|*/package.json|package-lock.json|*/package-lock.json|pnpm-lock.yaml|*/pnpm-lock.yaml|yarn.lock|*/yarn.lock|Cargo.toml|*/Cargo.toml|Cargo.lock|*/Cargo.lock|go.mod|*/go.mod|go.sum|*/go.sum|pom.xml|*/pom.xml|build.gradle|*/build.gradle|build.gradle.kts|*/build.gradle.kts|DESCRIPTION|*/DESCRIPTION|poetry.lock|*/poetry.lock|pdm.lock|*/pdm.lock|Pipfile|*/Pipfile|Pipfile.lock|*/Pipfile.lock|setup.py|*/setup.py|setup.cfg|*/setup.cfg|environment.yml|*/environment.yml|environment.yaml|*/environment.yaml|bun.lock|*/bun.lock|bun.lockb|*/bun.lockb|deno.lock|*/deno.lock|Gemfile|*/Gemfile|Gemfile.lock|*/Gemfile.lock|gems.rb|*/gems.rb|gems.locked|*/gems.locked|composer.json|*/composer.json|composer.lock|*/composer.lock|conanfile.txt|*/conanfile.txt|conanfile.py|*/conanfile.py|conan.lock|*/conan.lock|vcpkg.json|*/vcpkg.json|packages.config|*/packages.config|packages.lock.json|*/packages.lock.json|deps.json|*/deps.json|Directory.Packages.props|*/Directory.Packages.props|settings.gradle|*/settings.gradle|settings.gradle.kts|*/settings.gradle.kts|gradle.lockfile|*/gradle.lockfile|buildscript-gradle.lockfile|*/buildscript-gradle.lockfile|libs.versions.toml|*/libs.versions.toml|verification-metadata.xml|*/verification-metadata.xml|mix.exs|*/mix.exs|mix.lock|*/mix.lock|pubspec.yaml|*/pubspec.yaml|pubspec.lock|*/pubspec.lock|Package.swift|*/Package.swift|Package.resolved|*/Package.resolved|Podfile|*/Podfile|Podfile.lock|*/Podfile.lock|Cartfile.resolved|*/Cartfile.resolved|renv.lock|*/renv.lock|stack.yaml.lock|*/stack.yaml.lock|flake.lock|*/flake.lock|go.work|*/go.work|go.work.sum|*/go.work.sum|deno.json|*/deno.json|deno.jsonc|*/deno.jsonc|MODULE.bazel|*/MODULE.bazel|MODULE.bazel.lock|*/MODULE.bazel.lock|WORKSPACE|*/WORKSPACE|WORKSPACE.bazel|*/WORKSPACE.bazel|maven_install.json|*/maven_install.json|Manifest.toml|*/Manifest.toml|Project.toml|*/Project.toml|.terraform.lock.hcl|*/.terraform.lock.hcl|*.MODULE.bazel|*.tf|*.tofu|*.csproj|*.fsproj|*.vbproj|*.gemspec|*.nuspec) deps=true ;;
esac
done <<<"$changed"
else
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/strix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ jobs:
*) code=true ;;
esac
case "$changed_path" in
requirements*.txt|*/requirements*.txt|pyproject.toml|*/pyproject.toml|uv.lock|*/uv.lock|pylock.*.toml|*/pylock.*.toml|package.json|*/package.json|package-lock.json|*/package-lock.json|pnpm-lock.yaml|*/pnpm-lock.yaml|yarn.lock|*/yarn.lock|Cargo.toml|*/Cargo.toml|Cargo.lock|*/Cargo.lock|go.mod|*/go.mod|go.sum|*/go.sum|pom.xml|*/pom.xml|build.gradle|*/build.gradle|build.gradle.kts|*/build.gradle.kts|DESCRIPTION|*/DESCRIPTION) deps=true ;;
requirements*.txt|*/requirements*.txt|pyproject.toml|*/pyproject.toml|uv.lock|*/uv.lock|pylock.*.toml|*/pylock.*.toml|package.json|*/package.json|package-lock.json|*/package-lock.json|pnpm-lock.yaml|*/pnpm-lock.yaml|yarn.lock|*/yarn.lock|Cargo.toml|*/Cargo.toml|Cargo.lock|*/Cargo.lock|go.mod|*/go.mod|go.sum|*/go.sum|pom.xml|*/pom.xml|build.gradle|*/build.gradle|build.gradle.kts|*/build.gradle.kts|DESCRIPTION|*/DESCRIPTION|poetry.lock|*/poetry.lock|pdm.lock|*/pdm.lock|Pipfile|*/Pipfile|Pipfile.lock|*/Pipfile.lock|setup.py|*/setup.py|setup.cfg|*/setup.cfg|environment.yml|*/environment.yml|environment.yaml|*/environment.yaml|bun.lock|*/bun.lock|bun.lockb|*/bun.lockb|deno.lock|*/deno.lock|Gemfile|*/Gemfile|Gemfile.lock|*/Gemfile.lock|gems.rb|*/gems.rb|gems.locked|*/gems.locked|composer.json|*/composer.json|composer.lock|*/composer.lock|conanfile.txt|*/conanfile.txt|conanfile.py|*/conanfile.py|conan.lock|*/conan.lock|vcpkg.json|*/vcpkg.json|packages.config|*/packages.config|packages.lock.json|*/packages.lock.json|deps.json|*/deps.json|Directory.Packages.props|*/Directory.Packages.props|settings.gradle|*/settings.gradle|settings.gradle.kts|*/settings.gradle.kts|gradle.lockfile|*/gradle.lockfile|buildscript-gradle.lockfile|*/buildscript-gradle.lockfile|libs.versions.toml|*/libs.versions.toml|verification-metadata.xml|*/verification-metadata.xml|mix.exs|*/mix.exs|mix.lock|*/mix.lock|pubspec.yaml|*/pubspec.yaml|pubspec.lock|*/pubspec.lock|Package.swift|*/Package.swift|Package.resolved|*/Package.resolved|Podfile|*/Podfile|Podfile.lock|*/Podfile.lock|Cartfile.resolved|*/Cartfile.resolved|renv.lock|*/renv.lock|stack.yaml.lock|*/stack.yaml.lock|flake.lock|*/flake.lock|go.work|*/go.work|go.work.sum|*/go.work.sum|deno.json|*/deno.json|deno.jsonc|*/deno.jsonc|MODULE.bazel|*/MODULE.bazel|MODULE.bazel.lock|*/MODULE.bazel.lock|WORKSPACE|*/WORKSPACE|WORKSPACE.bazel|*/WORKSPACE.bazel|maven_install.json|*/maven_install.json|Manifest.toml|*/Manifest.toml|Project.toml|*/Project.toml|.terraform.lock.hcl|*/.terraform.lock.hcl|*.MODULE.bazel|*.tf|*.tofu|*.csproj|*.fsproj|*.vbproj|*.gemspec|*.nuspec) deps=true ;;
esac
done <<<"$changed"
else
Expand Down
64 changes: 64 additions & 0 deletions tests/test_changed_scope_dependency_manifest_allowlist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Contract: the `changed-scope` dependency allowlist covers scannable manifests.

`security-scan.yml` runs `osv-scan` and `dependency-review` only when the
`changed-scope` classifier sets `deps=true`, and that flag is restored from a
closed filename allowlist. A dependency introduced through a manifest or
lockfile missing from the list silently skips both supply-chain gates (Strix
MEDIUM finding on `.github#2143`, 2026-09-13). The list must therefore cover
every lockfile the pinned osv-scanner v2.5.1 can scan plus the manifests the
GitHub dependency graph reads. The three gate copies share one byte-identical
classifier block (see `test_docs_only_pr_runner_admission.py`), so this test
reads the line from each copy.
"""

from __future__ import annotations

from pathlib import Path

WORKFLOWS_DIR = Path(__file__).resolve().parents[1] / ".github/workflows"
GATE_COPIES = ("security-scan.yml", "sast-semgrep.yml", "strix.yml")

# osv-scanner v2.5.1 `docs/supported_languages_and_lockfiles.md` names, plus
# the manifests that declare dependencies before any lockfile exists.
REQUIRED_MANIFESTS = (
"requirements*.txt", "pyproject.toml", "uv.lock", "pylock.*.toml", "poetry.lock", "pdm.lock",
"Pipfile", "Pipfile.lock", "setup.py", "setup.cfg", "environment.yml",
"package.json", "package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lock", "bun.lockb", "deno.lock",
"Cargo.toml", "Cargo.lock", "go.mod", "go.sum", "go.work", "go.work.sum",
"pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", "gradle.lockfile",
"buildscript-gradle.lockfile", "libs.versions.toml", "verification-metadata.xml",
"Gemfile", "Gemfile.lock", "gems.locked", "composer.json", "composer.lock", "conan.lock", "conanfile.txt",
"packages.lock.json", "packages.config", "deps.json", "Directory.Packages.props",
"mix.exs", "mix.lock", "pubspec.yaml", "pubspec.lock", "Package.swift", "Package.resolved",
"Podfile", "Podfile.lock", "renv.lock", "DESCRIPTION", "stack.yaml.lock", "flake.lock", "vcpkg.json",
"deno.json", "deno.jsonc", "MODULE.bazel", "MODULE.bazel.lock", "WORKSPACE", "WORKSPACE.bazel",
"maven_install.json", "Manifest.toml", "Project.toml", ".terraform.lock.hcl",
)
REQUIRED_SUFFIX_GLOBS = ("*.csproj", "*.fsproj", "*.vbproj", "*.gemspec", "*.nuspec", "*.MODULE.bazel", "*.tf", "*.tofu")


def _deps_case_line(workflow_name: str) -> str:
"""Return the single `case` pattern line that restores `deps=true`."""
lines = [
line.strip()
for line in (WORKFLOWS_DIR / workflow_name).read_text(encoding="utf-8").splitlines()
if line.rstrip().endswith(") deps=true ;;")
]
assert len(lines) == 1, (workflow_name, len(lines))
return lines[0]


def test_dependency_allowlist_names_every_scannable_manifest() -> None:
"""Each gate copy lists every manifest at the root and under any directory."""
for workflow_name in GATE_COPIES:
patterns = set(_deps_case_line(workflow_name).split(") deps=true")[0].split("|"))
for manifest in REQUIRED_MANIFESTS:
assert manifest in patterns, (workflow_name, manifest)
assert f"*/{manifest}" in patterns, (workflow_name, manifest)
for glob in REQUIRED_SUFFIX_GLOBS:
assert glob in patterns, (workflow_name, glob)


def test_dependency_allowlist_is_identical_across_gate_copies() -> None:
"""The allowlist must not drift between the three classifier copies."""
assert len({_deps_case_line(name) for name in GATE_COPIES}) == 1
Loading