Skip to content

Support Parallel Workflow Steps + More Lenient Parsing#445

Draft
SUSTAPLE117 wants to merge 3 commits into
mainfrom
maint/improvedYamlParsing
Draft

Support Parallel Workflow Steps + More Lenient Parsing#445
SUSTAPLE117 wants to merge 3 commits into
mainfrom
maint/improvedYamlParsing

Conversation

@SUSTAPLE117

@SUSTAPLE117 SUSTAPLE117 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens the GitHub Actions workflow/action parser (models/github_actions.go) on two fronts, benchmarked against GitHub's official workflow-parser: it no longer drops whole files on locally-malformed YAML, and it now understands the new parallel: step blocks.

1. Lenient, per-node parsing (no more silent file drops)

Problem: ParseFromMemory discards an entire workflow whenever yaml.Unmarshal returns an error, and many custom UnmarshalYAML methods returned an error on a single odd sub-node. So one malformed field (a permission typo, an empty cron, a wrong-shaped branches, etc.) silently removed the whole workflow from the scan — a false-negative / scan-evasion risk. GitHub's own parser records errors but keeps a partial result.

Fix: every error-returning branch in the custom unmarshalers now degrades gracefully — it skips the offending sub-tree (or that one job/step/event) and keeps parsing the rest, matching the pattern already used by GithubActionsStrategy. Touched: Jobs, JobSecrets, StringList, Events (schedule/cron), Outputs, Inputs, Envs, Permissions (unknown scalar → empty), JobRunsOn, Container, Environment. Genuinely malformed YAML (e.g. []], a lexer-level syntax error) still surfaces as an error.

A new lenient GithubActionsSteps.UnmarshalYAML was added so that a single malformed step drops only that step instead of failing its whole job.

2. Support for parallel: step blocks

GitHub recently added parallel steps:

steps:
  - uses: actions/checkout@v6
  - parallel:
      - run: npm run build:frontend
      - run: npm run build:backend
  - run: npm test

Previously a parallel: item decoded into an empty husk step and every nested run/uses sink vanished — invisible to all rego rules (injection, untrusted-checkout-exec, known-vuln, etc.).

The parser now flattens parallel children inline into the steps list (recursively, so nested parallel works too). Because every rule and the inventory already iterate the flat job.steps[_] / action.runs.steps[_], this needs zero rego changes and composite actions are covered for free. Per-step line numbers are preserved. Parallelism ordering/grouping is intentionally not modeled — no rule needs it.

Tests

  • Reworked TestGithubActionsWorkflowJobs / TestGithubActionsWorkflowEvents to encode the new lenient contract (added a Dropped flag distinguishing "sub-tree skipped, no error" from genuine YAML syntax errors).
  • TestGithubActionsWorkflowLenientResilience — a workflow combining a wrong-shaped branches, an empty cron, an invalid permission scalar, a malformed step, and an undecodable job; asserts it still parses, stays valid, and retains all well-formed content.
  • TestGithubWorkflowMalformedNotDropped (scanner) — proves ParseFromMemory retains a malformed-but-valid workflow.
  • TestGithubActionsParallelStepsFlattened — the doc example flattens to 4 visible steps (+ nested-parallel case).

Full suite green including snapshot regression tests; make fmt / make lint-branch clean.

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.

1 participant