Skip to content

Commit 18acaec

Browse files
committed
Fix reusable workflow readiness integration
1 parent 4fb9c4a commit 18acaec

18 files changed

Lines changed: 111 additions & 28 deletions

docs/adoption.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ display names, repository/ref-qualified values, and paths outside that directory
3737
with exact paths. Producers must expose the full workflow ref and protection
3838
state; reusable producers must also expose the caller workflow ref.
3939

40+
Process 1.4.1 removes that caller-ref requirement because GitHub does not provide
41+
it as a default runner variable. Reusable workflows authorize the exact called
42+
workflow in `GITHUB_WORKFLOW_REF`; configure allowed callers with GitHub-native
43+
repository controls. Regenerate effective obligations so readiness receives the
44+
selected `adapter_mapping`, including any non-`main` protected branch.
45+
4046
Optional `adapters.github_copilot.scoped_instructions` generate compact
4147
path-scoped `.github/instructions/*.instructions.md` files from canonical process
4248
identity and declared local-context paths. They are hash-locked and non-canonical.

docs/capability-status.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Capability status
22

3-
Current process release: **1.4.0**.
3+
Current process release: **1.4.1**.
44

55
Status terms: **implemented** is executable and tested; **partial** works with stated prerequisites/limits; **experimental** is usable but compatibility may change; **designed** has an accepted contract but no complete implementation; **deferred** is intentionally postponed with tracked rationale. Material implementation and release changes must update this matrix.
66

docs/evidence-lifecycle.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,9 @@ Version 1.4.0 removes free-form and substring GitHub workflow authorization.
8585
Adopters keep exact `.github/workflows/*.yml` allowlist paths, provide the full
8686
GitHub workflow ref and protected-ref state, and provide the caller workflow ref
8787
for reusable workflows. Ambiguous legacy values fail with migration diagnostics.
88+
89+
Version 1.4.1 corrects the reusable-workflow and protected-branch integration.
90+
Reusable calls authorize GitHub's exact called-workflow ref; caller permission is
91+
delegated to GitHub and repository-native controls rather than claimed as
92+
independently verified. Readiness consumes the serialized `adapter_mapping`, so a
93+
configured non-`main` protected branch is identical at production and join time.

enforcement/github/reference-flow.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ configured protected/default branch with `GITHUB_REF_PROTECTED=true`. A workflow
2525
SHA equal to the assessed target cannot bootstrap its own authorization; a
2626
reviewed authorization change affects later runs. `pull_request` is diagnostic
2727
only. `pull_request_target` and `workflow_dispatch` must use the protected base
28-
definition. `workflow_call` also requires `GITHUB_CALLER_WORKFLOW_REF`, with both
29-
caller and callee allowlisted. Fork review runs in the authorized base repository.
30-
Reruns bind both run ID and attempt.
28+
definition. For `workflow_call`, the exact called reusable-workflow identity in
29+
`GITHUB_WORKFLOW_REF` is the identity authorized here. GitHub resolves that
30+
workflow; caller authorization remains a repository ruleset/caller-permissions
31+
responsibility and is outside this adapter's trust claim. No undocumented caller
32+
environment variable is required. Fork review runs in the authorized base
33+
repository. Reruns bind both run ID and attempt.
3134

3235
An independent reviewer runs `review-attest` from a protected review workflow
3336
after inspecting the objective artifact, then uploads

policy/core.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
schema_version: 1
22
process:
3-
version: 1.4.0
3+
version: 1.4.1
44
source: suyog19/software-engineering-process
55
controls:
66
traceability:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "software-engineering-process"
7-
version = "1.4.0"
7+
version = "1.4.1"
88
description = "Technology-neutral engineering policy and assurance platform"
99
readme = "README.md"
1010
requires-python = ">=3.11"

schemas/effective-obligations.schema.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
{
22
"$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object",
3-
"required": ["process", "repository", "classification", "required_capabilities", "required_evidence", "selected_skills", "native_enforcement", "prohibited_actions"],
3+
"required": ["process", "repository", "classification", "required_capabilities", "required_evidence", "selected_skills", "native_enforcement", "prohibited_actions", "adapter_mapping"],
44
"properties": {
55
"process": {"type": "object"}, "repository": {"type": "object"}, "classification": {"type": "object"},
66
"required_capabilities": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
77
"required_evidence": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
88
"selected_skills": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
99
"approvals": {"type": "array", "items": {"type": "string"}},
1010
"native_enforcement": {"type": "object"}, "prohibited_actions": {"type": "array", "items": {"type": "string"}},
11+
"adapter_mapping": {"type": ["object", "null"], "additionalProperties": false,
12+
"required": ["platform"], "properties": {"platform": {"const": "github"},
13+
"readiness_status": {"type": "string"}, "protected_branch": {"type": "string", "minLength": 1},
14+
"production_environment": {"type": "string"}, "runner_labels": {"type": "array", "items": {"type": "string"}}}},
1115
"not_required": {"type": "array", "items": {"type": "string"}}
1216
}
1317
}
14-
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Software Engineering Policy and Assurance platform."""
22

3-
__version__ = "1.4.0"
3+
__version__ = "1.4.1"

src/engineering_process/evidence.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,15 @@ def verify_readiness(obligations: dict, attestations: Iterable[dict], schema: di
175175
allowed_key = "trusted_ci_workflows" if record.get("producer_class") == "trusted-ci" else "authorized_review_workflows"
176176
if record["workflow_repository"].lower() != str(repository).lower() or record["workflow_path"] not in repo.get(allowed_key, []):
177177
raise ProcessError("GitHub provenance workflow identity is not authorized by resolved repository policy")
178-
protected_branch = obligations.get("adapters", {}).get("github", {}).get("protected_branch", "main")
178+
mapping = obligations.get("adapter_mapping")
179+
if mapping is None:
180+
protected_branch = "main"
181+
elif not isinstance(mapping, dict) or mapping.get("platform") != "github":
182+
raise ProcessError("GitHub provenance requires a valid GitHub adapter_mapping")
183+
else:
184+
protected_branch = mapping.get("protected_branch", "main")
185+
if not isinstance(protected_branch, str) or not protected_branch.strip():
186+
raise ProcessError("GitHub adapter_mapping protected_branch must be a non-empty string")
179187
workflow_ref = record["workflow_ref"]
180188
immutable = len(workflow_ref) == 40 and all(char in "0123456789abcdef" for char in workflow_ref)
181189
protected_default = workflow_ref == f"refs/heads/{protected_branch}" and record.get("ref_protected") is True

src/engineering_process/evidence_generation.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,6 @@ def _workflow_identity(manifest: dict, target_sha: str) -> dict:
7171
raise ProcessError(f"pull_request workflow content is not trusted; permitted ref rule: {permitted}")
7272
if immutable and parsed["workflow_ref"] == target_sha:
7373
raise ProcessError("a target revision cannot bootstrap trust from its own workflow revision; approve the trust change for subsequent runs")
74-
if event == "workflow_call":
75-
caller = _parse_workflow_ref(os.environ.get("GITHUB_CALLER_WORKFLOW_REF"))
76-
if caller["repository"].lower() != str(expected).lower():
77-
raise ProcessError("workflow_call caller repository does not match the configured repository")
78-
required["caller"] = caller
7974
if not (immutable or protected_default):
8075
raise ProcessError(f"unauthorized workflow ref {parsed['workflow_ref']!r}; permitted ref rule: {permitted}")
8176
return required
@@ -87,8 +82,7 @@ def _require_authorized_workflow(manifest: dict, workflow: dict, key: str) -> No
8782
normalized = [_normalized_workflow_path(path) for path in allowed]
8883
except (ProcessError, TypeError) as exc:
8984
raise ProcessError(f"ambiguous legacy configuration for {key}; use exact .github/workflows/*.yml paths: {exc}") from exc
90-
identities = [workflow] + ([workflow["caller"]] if "caller" in workflow else [])
91-
rejected = [identity["workflow_path"] for identity in identities if identity["workflow_path"] not in normalized]
85+
rejected = [workflow["workflow_path"]] if workflow["workflow_path"] not in normalized else []
9286
if not normalized or rejected:
9387
raise ProcessError(f"GitHub workflow is not authorized for {key}: repository={workflow['repository']!r}; "
9488
f"workflow_path={workflow['workflow_path']!r}; workflow_ref={workflow['workflow_ref']!r}; "

0 commit comments

Comments
 (0)