Skip to content

Test-DependencyPinning.ps1 missing per-violation Write-Host and Write-CIAnnotation #631

@WilliamBerryiii

Description

@WilliamBerryiii

Summary

Test-DependencyPinning.ps1 verifies SHA pinning compliance for supply chain security. It has Write-CIStepSummary (line 782) ✅ but is missing two CI output features: no Write-Host output at all for per-violation details, and Write-CIAnnotation fires only in the catch block (line 923). Contributors see results only via the step summary, artifacts, or by expanding verbose workflow logs — there is no console-level per-violation output.

Current Behavior

  • Console output: No Write-Host calls in the entire script (0 matches confirmed). ❌
  • CI annotations: Write-CIAnnotation only in catch block (line 923) — fires on fatal errors, not per unpinned dependency. ❌
  • Step summary: Write-CIStepSummary on line 782 with compliance score, unpinned count, and total deps. ✅
  • Workflow: dependency-pinning-scan.yml has a separate "Add job summary" step writing GITHUB_STEP_SUMMARY in YAML and inline ::warning annotations — partially compensating for the script's gaps.

Expected Behavior

  1. Per-violation Write-Host output in the console log showing each unpinned dependency with file path, action/image reference, and current pinning status — following the pattern from Invoke-PSScriptAnalyzer.ps1.
  2. Per-violation Write-CIAnnotation (level Warning) so GitHub renders inline annotations on PR diffs showing exactly which workflow lines have unpinned dependencies.

Root Cause

The script was designed around structured output formats (JSON, SARIF, CSV, Markdown, Table) written to files, with the workflow handling summary display via separate steps and inline ::warning notation. Console-level Write-Host output for human readability and per-violation Write-CIAnnotation for PR annotations were never implemented.

Files Requiring Changes

File Change
scripts/security/Test-DependencyPinning.ps1 Add per-violation Write-Host output in the results processing section. Add per-violation Write-CIAnnotation calls with Warning level.
scripts/tests/security/Test-DependencyPinning.Tests.ps1 Add mocks and assertions for Write-Host, Write-CIAnnotation, and existing Write-CIStepSummary.

Additional Context

  • Write-Host is safe — PSAvoidUsingWriteHost is explicitly excluded in scripts/linting/PSScriptAnalyzer.psd1.
  • The CIHelpers module is already available — no new imports needed.
  • The workflow dependency-pinning-scan.yml has inline ::warning annotations in YAML — the script-level Write-CIAnnotation calls will provide richer, per-file annotations that replace the need for workflow-level inline warnings.
  • The workflow's separate "Add job summary" step is redundant with the script's existing Write-CIStepSummary — consolidation is optional but out of scope for this issue.
  • Also called from weekly-security-maintenance.yml.

Fix Guidance

Per-Violation Console Output

In the results processing section, after violations are collected:

if ($unpinnedDeps.Count -gt 0) {
    Write-Host "`n❌ Found $($unpinnedDeps.Count) unpinned dependencies:" -ForegroundColor Red
    $groupedByFile = $unpinnedDeps | Group-Object -Property File
    foreach ($fileGroup in $groupedByFile) {
        Write-Host "`n📄 $($fileGroup.Name)" -ForegroundColor Cyan
        foreach ($dep in $fileGroup.Group) {
            Write-Host "  ⚠️ Line $($dep.Line): $($dep.Reference) (type: $($dep.Type))" -ForegroundColor Yellow
        }
    }
}
else {
    Write-Host "✅ All dependencies are properly SHA-pinned." -ForegroundColor Green
}

Per-Violation Annotations

After each Write-Host violation line:

Write-CIAnnotation -Message "Unpinned dependency: $($dep.Reference) (type: $($dep.Type))" `
    -Level Warning `
    -File $dep.File `
    -Line $dep.Line

Unit Testing and Code Coverage Requirements

Codecov Configuration

The repository enforces an auto-incrementing project coverage threshold (+1% over base) and an 80% patch target (codecov.yml). All new or modified lines must meet the patch coverage gate.

Pester Coverage

  • Config: scripts/tests/pester.config.ps1 — JaCoCo format, CoveragePercentTarget = 80
  • Coverage path: scripts/security/ is already in the coverage scan scope
  • Run: npm run test:ps

Current Test Gap

The existing test file scripts/tests/security/Test-DependencyPinning.Tests.ps1 has no mocks for Write-CIAnnotation, Write-CIStepSummary, or Write-Host. All CI helper mock infrastructure must be built from scratch:

  1. Mock Write-Host — the script currently has zero Write-Host calls. New per-violation output requires mocks with -ParameterFilter to verify each unpinned dependency is logged with file, line, and reference.
  2. Mock Write-CIAnnotation — currently catch-only. New per-violation calls require mock assertions for correct -File, -Line, -Level Warning, and -Message per unpinned dependency.
  3. Mock Write-CIStepSummary — already called at line 782 but never tested. Add mock and Should -Invoke assertion verifying the markdown content includes compliance score and dependency counts.

RPI Phase Testing Guidance

  • Research: Audit Test-DependencyPinning.Tests.ps1 for CI helper coverage gaps; document the results processing flow and the existing Write-CIStepSummary call.
  • Plan: Design test cases for per-violation Write-Host, per-violation Write-CIAnnotation, and existing Write-CIStepSummary content verification.
  • Implement: Add mock infrastructure for all CI helper functions; verify npm run test:ps passes with patch coverage ≥ 80%.
  • Review: Confirm no coverage regressions in the pester flag on Codecov.

RPI Framework Starter Prompts

Research Phase

Research CI output coverage in scripts/security/Test-DependencyPinning.ps1. Document: (1) the complete absence of Write-Host calls, (2) the catch-only Write-CIAnnotation pattern (line 923), (3) the existing Write-CIStepSummary call (line 782) and its markdown content, (4) the workflow dependency-pinning-scan.yml compensating with inline ::warning annotations and a separate "Add job summary" step, (5) the weekly-security-maintenance.yml usage, (6) existing Pester test coverage and the complete absence of CI helper mocks, and (7) codecov.yml and scripts/tests/pester.config.ps1 coverage requirements (80% patch target). Identify the results processing section where per-violation Write-Host and Write-CIAnnotation calls should be inserted.

Plan Phase

Plan CI output improvements for Test-DependencyPinning.ps1. The plan should cover: (1) adding per-violation Write-Host output grouped by file following the Invoke-PSScriptAnalyzer.ps1 pattern, (2) adding per-violation Write-CIAnnotation calls with Warning level, file path, and line number, (3) determining the correct insertion points in the results processing flow (before the existing Write-CIStepSummary call), (4) adding Pester tests with mocks for Write-Host, Write-CIAnnotation (per-violation), and Write-CIStepSummary (already exists but untested), and (5) ensuring patch coverage meets the 80% codecov gate.

Implement Phase

Implement CI output improvements for Test-DependencyPinning.ps1. Steps: (1) In the results processing section, after violations are collected and before Write-CIStepSummary (line 782), add grouped-by-file Write-Host output with 📄 file headers (Cyan) and ⚠️ per-violation detail lines (Yellow). (2) After each Write-Host violation line, add Write-CIAnnotation -Message "..." -Level Warning -File $file -Line $line. (3) In Test-DependencyPinning.Tests.ps1, add mocks for Write-Host, Write-CIAnnotation, and Write-CIStepSummary with -ParameterFilter assertions; ensure patch coverage ≥ 80%. Run npm run lint:ps and npm run test:ps to validate.

Review Phase

Review CI output changes to Test-DependencyPinning.ps1. Verify: (1) Write-Host provides grouped-by-file output with per-violation detail lines, (2) Write-CIAnnotation is called once per unpinned dependency with Warning level and correct file/line, (3) existing Write-CIStepSummary call is unmodified, (4) Pester tests mock and assert all three CI helpers, (5) npm run lint:ps passes, (6) no regressions in existing tests, (7) patch coverage meets the 80% codecov gate, (8) output formats align with other scripts for consistency, and (9) the workflow's separate summary step remains functional alongside the script's native Write-CIStepSummary.


References

  • Affected script: scripts/security/Test-DependencyPinning.ps1
  • Tests: scripts/tests/security/Test-DependencyPinning.Tests.ps1
  • Workflow: .github/workflows/dependency-pinning-scan.yml
  • Also used by: .github/workflows/weekly-security-maintenance.yml
  • Reference implementation: scripts/linting/Invoke-PSScriptAnalyzer.ps1 (per-violation output pattern)
  • Step summary reference: scripts/security/Test-DependencyPinning.ps1 line 782 (already implemented)
  • CI helpers module: scripts/lib/Modules/CIHelpers.psm1
  • Codecov config: codecov.yml (80% patch target, auto +1% project threshold)
  • Pester config: scripts/tests/pester.config.ps1 (JaCoCo format, 80% coverage target)

Metadata

Metadata

Labels

bugSomething isn't workinggithub-actionsGitHub Actions workflowsgood first issueGood for newcomersscriptsPowerShell, Bash, or Python scriptssecuritySecurity-related changes or concerns

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions