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
42 changes: 42 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Code scanning for this repository. Advanced setup: the caller below is the
# only scanner, and CodeQL default setup is deliberately left `not-configured`
# because default setup cannot express a pinned reusable and cannot be reviewed
# in a diff. Attachment is atomic — enabling default setup later means disabling
# this workflow first, or the whole attachment fails.
#
# Verify rather than trust this comment:
# GET /repos/{owner}/{repo}/code-scanning/default-setup -> not-configured
# GET /repos/{owner}/{repo}/actions/workflows -> this file's state
name: codeql

on:
push:
branches: [main]
pull_request:
schedule:
# Weekly, so a new query release is applied to unchanged code. Without this
# a repository that stops changing also stops being scanned.
- cron: '0 5 * * 2'

permissions: {}

concurrency:
group: codeql-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
analyze:
name: codeql
permissions:
actions: read
contents: read
security-events: write
uses: NDDev-OpenNetwork/ci-workflows/.github/workflows/public-codeql.yml@b50364e2a415267688c1d845cea6866cdb5e53d6 # 0.1.4
with:
# Public repository: `pull_request` runs untrusted fork code. Name the
# hosted runner explicitly — the reusable's default belongs to the pinned
# commit, not to this repository, so inheriting it would let a pin bump
# move fork pull requests onto private infrastructure with no diff here.
runner: ubuntu-latest
# Python is 75% of this repository; `actions` covers seven deployment workflows that hold apply and rollback authority.
languages: '["python","actions"]'
3 changes: 3 additions & 0 deletions catalog/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ actions:
- name: actions/upload-artifact
sha: "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"
version: "v7.0.1"
- name: NDDev-OpenNetwork/ci-workflows/.github/workflows/public-codeql.yml
sha: "b50364e2a415267688c1d845cea6866cdb5e53d6"
version: "0.1.4"
42 changes: 34 additions & 8 deletions scripts/validate_module.sh
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,46 @@ python3 - <<'PYCHECK'
from pathlib import Path
import re

# Read strictly, by column. The first version of this stripped every line and
# looked for `- name:` anywhere, which ignores indentation entirely -- so a
# catalog that no YAML parser will load still "read fine" and the whole check
# below passed on it. That was found by mis-indenting an entry by two spaces:
# `yaml.safe_load` raised `expected <block end>`, and this script printed OK.
# The module ships no dependencies, so the answer is not PyYAML; it is refusing
# any shape other than the one shape this file is allowed to have.
registry = {}
current = {}
for line in Path("catalog/actions.yml").read_text(encoding="utf-8").splitlines():
stripped = line.strip()
if stripped.startswith("- name:"):
catalog = Path("catalog/actions.yml")
in_actions = False
for number, line in enumerate(catalog.read_text(encoding="utf-8").splitlines(), 1):
if not line.strip() or line.lstrip().startswith("#"):
continue
if not in_actions:
in_actions = line == "actions:"
continue
if line.startswith(" - name: "):
if current:
registry[current["name"]] = current
current = {"name": stripped.split(":", 1)[1].strip().strip('"')}
elif stripped.startswith("sha:") and current:
current["sha"] = stripped.split(":", 1)[1].strip().strip('"')
elif stripped.startswith("version:") and current:
current["version"] = stripped.split(":", 1)[1].strip().strip('"')
current = {"name": line[len(" - name: "):].strip().strip('"')}
elif line.startswith(" sha: ") and current:
current["sha"] = line[len(" sha: "):].strip().strip('"')
elif line.startswith(" version: ") and current:
current["version"] = line[len(" version: "):].strip().strip('"')
else:
raise SystemExit(
f"{catalog}:{number}: catalog entries are exactly "
f'` - name: X` / ` sha: \"…\"` / ` version: \"…\"`; got {line!r}'
)
if current:
registry[current["name"]] = current
if not registry:
raise SystemExit(f"{catalog}: declares no actions; refusing to pass a check with nothing to check")
for name, entry in sorted(registry.items()):
missing = [k for k in ("sha", "version") if k not in entry]
if missing:
raise SystemExit(f"{catalog}: {name} is missing {', '.join(missing)}")
if len(entry["sha"]) != 40:
raise SystemExit(f"{catalog}: {name} records a {len(entry['sha'])}-character SHA, expected 40")

pattern = re.compile(r"uses:\s+([A-Za-z0-9_.-]+/[A-Za-z0-9_./-]+)@([0-9a-f]{40})\s*#\s*(\S+)")
seen = set()
Expand Down