Skip to content

Strict field validation and scoped per-workflow Parse - #3

Merged
nodeselector merged 2 commits into
mainfrom
nodeselector/strict-field-validation
Jun 12, 2026
Merged

Strict field validation and scoped per-workflow Parse#3
nodeselector merged 2 commits into
mainfrom
nodeselector/strict-field-validation

Conversation

@nodeselector

Copy link
Copy Markdown
Collaborator

What

Two changes that tighten lockfile validation and fix a blast-radius bug:

1. Zero-value rejectionParse now enforces that required action fields carry meaningful values, not just that the keys are present:

  • branch must be a non-empty string
  • owner_id and repo_id must be positive integers (> 0)

A present-but-zero-value field silently disables the security check it meant to enforce (e.g. owner_id: 0 bypasses owner identity verification). The JSON schema is tightened with minLength and minimum constraints to match.

2. Scoped per-workflow validationParse gains a variadic paths ...string parameter. Per-dependency checks (unknown keys, required keys, zero-value rejection) are scoped to the union of f.Workflows[p] for the requested paths:

  • Empty paths (zero args) — validate every dependency entry. Back-compatible for CLI regen, Dependabot, and any whole-file tooling. Existing callers compile unchanged.
  • Non-empty paths — only validate entries referenced by the named workflows. Entries outside that set are skipped.
  • A requested path absent from f.Workflows contributes zero entries and validates nothing — fail-open by design for non-onboarded workflows.

Document-level invariants (version required/supported, unknown top-level keys) always run regardless of paths.

canonicalizeActions is left global — a conflicting canonical pin with different metadata is a structural file defect, not a per-workflow concern.

Why

In co-located shared lockfiles, Parse validates the entire dependencies: map up front and returns on the first failure. Downstream consumers (actions-workflow-parser, launch) call Parse once per workflow being compiled, then look up just that workflow's pins. One corrupt dependency entry — even one no active workflow references — fails every workflow that loads the lockfile. A single bad entry takes down an entire repo's CI instead of just the workflow that actually consumes it.

Downstream

actions-workflow-parser will change its lockfile.Parse(contents) call to lockfile.Parse(contents, workflowKey) to scope validation per-workflow. That PR is up and validated against a dev pseudo-version of this commit; after this merges, the parser re-pins to a release, then launch re-pins to the parser.

Tests

17 new tests across both commits:

  • Zero-value rejection: empty branch, zero owner_id, zero repo_id, negative ID
  • Scoped validation matrix: no-paths back-compat, good-path-only OK, corrupt-path errors, absent-path fail-open, union of good+corrupt errors, unknown/zero-value fields in-scope vs out-of-scope, global invariants (bad version, unknown top-level key) still fire with scoped paths

Parse now enforces that required action fields carry meaningful values,
not just that the keys are present:

- branch must be a non-empty string
- owner_id and repo_id must be positive integers (> 0)

A present-but-zero-value field would silently disable the security check
it is meant to enforce (e.g. owner_id: 0 bypasses owner identity
verification). The JSON schema is tightened with minLength and minimum
constraints to match.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 12, 2026 14:46
GitHub Advanced Security started work on behalf of nodeselector June 12, 2026 14:47 View session
GitHub Advanced Security finished work on behalf of nodeselector June 12, 2026 14:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

Scoped validation currently matches dependencies using raw (pre-canonicalization) pin strings, which can incorrectly skip validation for referenced dependencies when casing differs between workflows entries and dependencies keys.

Pull request overview

Tightens the GitHub Actions lockfile parser/schema to reject meaningless zero values in required action metadata and adds a scoped-validation mode so consumers can validate only dependencies referenced by selected workflows (reducing blast radius in shared lockfiles).

Changes:

  • Tighten JSON schema constraints (branch minLength, owner_id/repo_id minimum) and align Go validation to reject empty/zero/negative required fields.
  • Add Parse(contents, paths... ) to scope per-dependency validation to dependencies referenced by specified workflows, while keeping document-level invariants global.
  • Add tests covering both zero-value rejection and scoped validation behavior.
File summaries
File Description
schema/lockfile-v0.0.1.json Adds minLength/minimum constraints to prevent empty/zero values in required fields.
go/pkg/lockfile/schema_test.go Adds unit tests for zero-value rejection and per-workflow scoped validation matrix.
go/pkg/lockfile/schema_gen.go Regenerates embedded schema constant to match updated root JSON schema.
go/pkg/lockfile/lockfile.go Implements scoped validation and new zero-value enforcement in parser.

Copilot's findings

Files not reviewed (1)

  • go/pkg/lockfile/schema_gen.go: Generated file
  • Files reviewed: 3/4 changed files
  • Comments generated: 2

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread go/pkg/lockfile/lockfile.go
Comment thread go/pkg/lockfile/lockfile.go
Add variadic paths parameter to Parse(contents, paths...) so callers
can limit per-dependency checks (unknown keys, required keys, zero-value
rejection) to only the entries referenced by the named workflows.

When paths is empty, every dependency is validated — back-compat for
whole-file tooling (CLI regen, Dependabot, existing callers compile
unchanged). When paths is non-empty, entries outside the referenced set
are skipped. A path absent from f.Workflows contributes zero entries
and validates nothing (fail-open for non-onboarded workflows).

Document-level invariants (version, unknown top-level keys) always run
regardless of paths.

This fixes the blast-radius bug where one corrupt dependency entry in a
shared lockfile fails every workflow that loads it, including workflows
that don't reference the corrupt entry at all.
@nodeselector
nodeselector force-pushed the nodeselector/strict-field-validation branch from eeb3453 to 8930c62 Compare June 12, 2026 14:54
GitHub Advanced Security started work on behalf of nodeselector June 12, 2026 14:55 View session
GitHub Advanced Security finished work on behalf of nodeselector June 12, 2026 14:56
@nodeselector
nodeselector merged commit cecd264 into main Jun 12, 2026
8 checks passed
nodeselector added a commit that referenced this pull request Jul 8, 2026
Strict field validation and scoped per-workflow Parse
@nodeselector
nodeselector deleted the nodeselector/strict-field-validation branch July 8, 2026 16:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants