Skip to content

Commit a8580d8

Browse files
algattikCopilot
andauthored
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/copilot-instructions.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@ The `Physical-AI RPI` umbrella (`.github/agents/physical-ai-rpi.agent.md`) and i
338338

339339
See [docs/reference/copilot-artifacts.md](../docs/reference/copilot-artifacts.md) for the full umbrella/worker rationale.
340340

341+
## hve-core Derived Files
342+
343+
Follow the baseline conventions in [`scripts/README.md`](../scripts/README.md). `scripts/security/Test-HveCoreFreshness.ps1` compares source-header entries with a resolved upstream `main` commit and release entries with the RPI `UPSTREAM_REF` and a resolved latest non-draft release commit.
344+
341345
## Git Workflow
342346

343347
Full specification in `.github/instructions/commit-message.instructions.md`.

.github/workflows/check-hve-core-freshness.yml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,35 @@ jobs:
3131
with:
3232
persist-credentials: false
3333

34-
- name: Check hve-core-derived files against latest release
34+
- name: Check hve-core-derived files against reviewed upstream baselines
3535
id: check
3636
env:
3737
GH_TOKEN: ${{ github.token }}
3838
run: ./scripts/security/Test-HveCoreFreshness.ps1
3939

4040
- name: Create or update tracking issue
41-
if: steps.check.outputs.stale-count != '0'
41+
if: "!cancelled() && steps.check.outputs.attention-count != '' && steps.check.outputs.attention-count != '0'"
4242
env:
4343
GH_TOKEN: ${{ github.token }}
44-
STALE_COUNT: ${{ steps.check.outputs.stale-count }}
44+
DRIFT_COUNT: ${{ steps.check.outputs.drift-count }}
45+
ERROR_COUNT: ${{ steps.check.outputs.error-count }}
4546
run: |
4647
. ./scripts/security/Test-HveCoreFreshness.ps1
4748
$data = Get-Content 'hve-core-freshness-results.json' -Raw | ConvertFrom-Json
4849
$runUrl = "$env:GITHUB_SERVER_URL/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID"
4950
$checkDate = Get-Date -Format 'yyyy-MM-dd' -AsUTC
5051
$body = Format-HveCoreIssueBody -Result $data -RunUrl $runUrl -CheckDate $checkDate
51-
$title = "security: hve-core upstream updates available ($env:STALE_COUNT stale)"
52+
$title = "security: hve-core freshness needs attention ($env:DRIFT_COUNT drifted, $env:ERROR_COUNT check errors)"
5253
$existing = Get-HveCoreTrackingIssue
53-
if ($existing) { gh issue edit $existing --title $title --body $body; gh issue comment $existing --body "🔄 Weekly scan: $env:STALE_COUNT item(s) stale as of $checkDate. [Workflow run]($runUrl)." }
54+
if ($existing) {
55+
gh issue edit $existing --title $title --body $body
56+
if ($LASTEXITCODE -ne 0) { throw "Could not update hve-core freshness issue #$existing" }
57+
gh issue comment $existing --body "🔄 Weekly scan: $env:DRIFT_COUNT drifted, $env:ERROR_COUNT check errors as of $checkDate. [Workflow run]($runUrl)."
58+
}
5459
else { gh issue create --title $title --body $body --label "dependencies,automated,needs-triage" }
5560
5661
- name: Close resolved tracking issue
57-
if: steps.check.outputs.stale-count == '0'
62+
if: steps.check.outputs.attention-count == '0'
5863
env:
5964
GH_TOKEN: ${{ github.token }}
6065
run: |

scripts/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ Security scanning and dependency management scripts.
7171
| `security/Test-DependencyPinning.ps1` | Validate dependency pinning compliance |
7272
| `security/Test-SHAStaleness.ps1` | Check for outdated SHA pins |
7373
| `security/Test-BinaryFreshness.ps1` | Validate pinned binary hashes and Helm chart versions; emits SARIF for GitHub Security tab |
74-
| `security/Test-HveCoreFreshness.ps1` | Check hve-core-derived modules against the latest upstream release |
74+
| `security/Test-HveCoreFreshness.ps1` | Check hve-core-derived files against their reviewed release or source-header baselines |
7575
| `security/zap-to-sarif.py` | Convert ZAP results to SARIF format |
7676
| `update-chart-hashes.sh` | Refresh pinned Helm chart versions and SHA-256 hashes in `infrastructure/setup/defaults.conf` |
7777

7878
The `Test-BinaryFreshness.ps1` script is invoked by the `check-binary-integrity.yml` workflow on a weekly schedule. It downloads each pinned GPG key, installer, and CLI archive, compares SHA-256 hashes against the values pinned in `.devcontainer/install-dev-deps.sh` and `.devcontainer/devcontainer.json`, and queries upstream Helm repositories for chart version drift. Findings are written to `binary-freshness-results.sarif` with per-rule `helpUri` values pointing at the appropriate remediation script.
7979

80-
The `Test-HveCoreFreshness.ps1` script is invoked by the `check-hve-core-freshness.yml` workflow on a weekly schedule. For every hve-core-derived module, it compares the **upstream** blob SHA at the pinned `UPSTREAM_REF` (the last reviewed upstream ref) against the newest non-draft `microsoft/hve-core` release. Comparing blobs, not release tags, means a stale signal is a real upstream change rather than an unrelated prerelease.
80+
The `Test-HveCoreFreshness.ps1` script is invoked by the `check-hve-core-freshness.yml` workflow on a weekly schedule. Each derived file declares a baseline. `release` files compare the **upstream** blob SHA at the pinned `UPSTREAM_REF` against one resolved, immutable newest non-draft release revision. `source-header` files compare the revision recorded in their header against one resolved, immutable upstream `main` revision. This reports relevant upstream changes before they appear in a release.
81+
82+
Source-header files must include `Adapted from microsoft/hve-core <upstream-path> as of commit <40-hex SHA>`. Comparing upstream blobs avoids false drift from intentional local adaptations.
8183

8284
### 🔗 Where Pins Live
8385

scripts/security/Test-DangerousWorkflow.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
the pull-request head ref, executing untrusted code in a privileged context.
1919
2020
Adapted from microsoft/hve-core scripts/security/Test-DangerousWorkflow.ps1
21-
as of commit b70237d08d5caf6918b9de9952a243a8588b92dc (2026-07-02).
21+
as of commit b70237d08d5caf6918b9de9952a243a8588b92dc.
2222
2323
Local divergences from upstream (each marked inline with a '# LOCAL' comment):
2424

0 commit comments

Comments
 (0)