Commit a8580d8
fix(scripts): extend hve-core linter freshness (#1391)
This PR extends the repository's weekly hve-core freshness check to
cover two vendored PowerShell security linters.
## Problem and Context
Issue #1133 documents a blind spot in the existing freshness check. The
check covered hve-core-derived PowerShell modules by comparing the
upstream blob at the repository's pinned hve-core release commit with
the blob in the newest release, but it did not cover
`Test-WorkflowPermissions.ps1` or `Test-DangerousWorkflow.ps1`.
Those linters cannot use one global release baseline. Their local
headers identify different reviewed upstream commits, and at least one
upstream source is available on `main` but not in a release. The local
files also contain intentional adaptations, so comparing local file
contents directly with upstream would report expected differences as
drift.
## Proposed Solution
The documented design uses two baseline types in one manifest:
| Baseline | Files | Comparison |
|----------|-------|------------|
| `release` | Existing derived modules | Upstream blob at the RPI
bootstrap's pinned `UPSTREAM_REF` versus the same path at the resolved
newest non-draft release commit |
| `source-header` | Vendored security linters | Upstream blob at the
exact commit recorded in the local file's provenance header versus the
same path at one resolved upstream `main` commit |
Both modes compare upstream blobs with upstream blobs. This is the
principal mechanism for excluding intentional local adaptations from the
drift signal.
The script distinguishes upstream drift from invalid local provenance.
Drift remains an issue-triaged signal with a successful process exit;
local validation errors produce exit code 2. The workflow receives
separate attention, drift, and error counters.
## Logical Changed Blocks
### Freshness manifest and provenance parsing
`scripts/security/Test-HveCoreFreshness.ps1` changes the derived-file
manifest from a path list to entries containing `Path` and `Baseline`.
It adds both security linters as `source-header` entries.
`Get-HveCoreFileSource` reads only the comment-based help header and
requires one exact provenance statement containing the hve-core path and
a full 40-character commit SHA. The recorded upstream path must match
the local manifest path exactly. These constraints prevent malformed or
misleading provenance from selecting the wrong upstream object. The two
linter headers remove their parenthesized dates because repository
comment policy prohibits temporal markers in code; the immutable commit
SHA remains the provenance identifier.
Issue #1133 requires a per-file source revision. The implementation
validates that revision and path before trusting them as comparison
inputs.
### Immutable upstream resolution and drift classification
The check resolves the latest non-draft release tag and upstream `main`
to immutable full commit SHAs once per run. It validates the pinned
release ref before checking individual files. Each manifest entry is
dispatched to its baseline-specific comparison.
The drift model now distinguishes current, changed, missing-at-baseline,
missing-at-target, missing-at-both, and local validation-error states. A
missing path at both refs is treated as invalid configuration. A missing
source-header path at its recorded commit is also treated as invalid
provenance.
Documented rationale: the PR description says each run should use one
immutable release commit and one immutable `main` commit, and should
separate upstream drift from local validation errors.
### GitHub API and rendering boundaries
GitHub API helpers validate returned commit and blob SHAs. API failures
other than a reported HTTP 404 throw instead of appearing as missing
files. This prevents transient or malformed API responses from creating
false drift issues. Release URLs are restricted to HTTPS URLs on
`github.com`, and external text is encoded before insertion into
Markdown tables and labels so API-controlled values cannot alter
generated issue or job-summary structure. The helper contracts, failure
messages, and focused tests make these trust-boundary requirements
explicit.
### Results, process status, and workflow outputs
The JSON result gains the resolved release and `main` SHAs plus
independent `DriftCount` and `ErrorCount` values. The script emits
`attention-count`, `drift-count`, and `error-count` to GitHub Actions.
Drift alone exits successfully; validation errors exit with code 2.
The workflow opens or updates a tracking issue whenever attention is
required, uses separate drift and error counts in its title and comment,
and closes the issue only when neither exists. It checks the exit status
after editing an existing issue before adding the weekly comment.
Documented rationale: the PR description defines drift as issue-triaged
and invalid local provenance as workflow-failing. The issue itself asks
only that drift be surfaced; the exact failure policy is a design choice
recorded in this branch and its PR description.
### Reports and documentation
Issue and job-summary tables identify each file's baseline type, exact
upstream comparison, blob SHAs, state, and validation error. Refresh
guidance tells maintainers to review upstream changes, preserve local
adaptations, and update a source-header commit after review.
`scripts/README.md` documents both baseline modes and the required
source-header syntax. `.github/copilot-instructions.md` records the same
maintenance convention for future coding agents.
### Test expansion
`scripts/tests/security/Test-HveCoreFreshness.Tests.ps1` expands
coverage across the complete manifest, provenance parsing, release
selection, API response validation, state classification, baseline
dispatch, Markdown rendering, orchestration, workflow-output consumers,
exit policy, and the configuration-preview entry point.
## Summary
- Compare release-baseline modules between the pinned `UPSTREAM_REF` and
one immutable resolved latest-release commit; compare source-header
security linters between their recorded source commit and one immutable
resolved upstream `main` commit.
- Report upstream drift and local validation errors separately in JSON,
workflow outputs, and job summaries.
- Keep upstream drift as an issue-triaged signal while failing the
workflow for invalid local provenance.
- Validate provenance paths, full commit SHAs, API responses, release
tags, release URLs, and Markdown rendering at trust boundaries.
- Enforce the script-to-workflow output contract and fail when
tracking-issue updates do not succeed.
- Expand Pester coverage for the complete manifest, API request shapes,
report formatting, orchestration, and workflow consumers.
## Validation
- Affected security Pester suites: 163 passed
- PSScriptAnalyzer: 4 changed PowerShell files, 0 errors, 0 warnings
- Runtime smoke tests: freshness preview, live comparison, malformed
provenance, clean scans, violations, and invalid paths
- `git diff --check`
Closes #1133
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent 5339ca9 commit a8580d8
7 files changed
Lines changed: 1203 additions & 134 deletions
File tree
- .github
- workflows
- scripts
- security
- tests/security
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
338 | 338 | | |
339 | 339 | | |
340 | 340 | | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
341 | 345 | | |
342 | 346 | | |
343 | 347 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
41 | | - | |
| 41 | + | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
| 44 | + | |
| 45 | + | |
45 | 46 | | |
46 | 47 | | |
47 | 48 | | |
48 | 49 | | |
49 | 50 | | |
50 | 51 | | |
51 | | - | |
| 52 | + | |
52 | 53 | | |
53 | | - | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
54 | 59 | | |
55 | 60 | | |
56 | 61 | | |
57 | | - | |
| 62 | + | |
58 | 63 | | |
59 | 64 | | |
60 | 65 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
74 | | - | |
| 74 | + | |
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
80 | | - | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
81 | 83 | | |
82 | 84 | | |
83 | 85 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| |||
0 commit comments