Skip to content

Commit 4e9e31f

Browse files
WilliamBerryiiiBill BerrybindsijakkajCopilot
authored
feat(build): enable npm pinning enforcement in dependency scan (#838)
## Description Enabled **npm pinning enforcement** in the CI dependency-pinning scan workflow. Updated the workflow default to scan `github-actions,npm,workflow-npm-commands`, activating automatic detection of unpinned npm commands in GitHub Actions workflow `run:` blocks. Three PowerShell functions were added to *Test-DependencyPinning.ps1*: - **`Test-NpmCommandLine`** — regex-based detection of unpinned npm commands (`npm install`, `npm i`, `npm update`, `npm install-test`) while excluding safe variants (`npm ci`, `npm run`, `npm test`, `npm audit`) - **`Get-WorkflowNpmCommandViolations`** — indentation-aware YAML parser that tracks nesting depth to confine detection to actual shell content and avoid flagging comments - **`New-NpmCommandViolation`** — constructs violation objects with Medium severity and remediation guidance Pester test coverage was added with fixture files for both safe and unsafe npm command patterns. ### Scope Change: npm Exact-Version Enforcement This PR introduces a **strategic departure** from SHA-pinning for npm dependencies. Unlike GitHub Actions (which use 40-character commit SHAs for immutable references), npm dependencies use **exact-version enforcement** (`X.Y.Z` with no `^`, `~`, `*`, or range operators). **Rationale for exact-version enforcement over SHA-pinning:** | Factor | GitHub Actions (SHA) | npm (Exact Version) | |--------|---------------------|---------------------| | Registry model | Git repositories with mutable tags | Immutable registry artifacts | | Integrity verification | SHA pins to specific commit | Lockfile integrity hashes (SHA-512) | | Audit compatibility | N/A | `npm audit` requires semver versions | | Reproducibility | SHA guarantees exact code | Lockfile + exact version guarantees exact code | | Human readability | 40-char hex strings | Semantic version numbers | The validation regex enforces exact versions: `^\d+\.\d+\.\d+$` — no ranges, no prefixes, no wildcards. ### Follow-Up Hardening (commit `b8bd099`) Additional improvements shipped on this branch: - **pip ExcludePatterns** — Virtual environment directories (`.venv`, `venv`, `.tox`, `.nox`, `__pypackages__`) are now excluded from pip dependency scanning to avoid false positives - **SARIF integration tests** — Expanded from 1 weak assertion to 8 focused tests validating SARIF 2.1.0 schema compliance, severity mapping (High→error, Medium→warning), location structure, and metadata properties - **Dependency pinning documentation** — New `docs/security/dependency-pinning.md` page documenting all pinning strategies with rationale, validation rules, CI integration flowchart, and a dedicated comparison of SHA-pinning vs exact-version enforcement for npm ## Related Issue(s) Closes #526 ## Type of Change Select all that apply: **Code &amp; Documentation:** * [ ] Bug fix (non-breaking change fixing an issue) * [x] New feature (non-breaking change adding functionality) * [ ] Breaking change (fix or feature causing existing functionality to change) * [x] Documentation update **Infrastructure &amp; Configuration:** * [x] GitHub Actions workflow * [ ] Linting configuration (markdown, PowerShell, etc.) * [x] Security configuration * [ ] DevContainer configuration * [ ] Dependency update **AI Artifacts:** * [ ] Reviewed contribution with `prompt-builder` agent and addressed all feedback * [ ] Copilot instructions (`.github/instructions/*.instructions.md`) * [ ] Copilot prompt (`.github/prompts/*.prompt.md`) * [ ] Copilot agent (`.github/agents/*.agent.md`) * [ ] Copilot skill (`.github/skills/*/SKILL.md`) **Other:** * [x] Script/automation (`.ps1`, `.sh`, `.py`) * [ ] Other (please describe): ## Testing Validated with 100 Pester tests covering: - Detected 4 violations in multi-command fixture (install, update, install-test, shorthand i) - Returned zero violations for safe-only workflow (ci, run, test, audit) - Handled nonexistent files gracefully - Excluded commented-out commands - Detected violations in multi-line run blocks with mixed safe/unsafe commands - SARIF 2.1.0 output: version, schema, tool driver, result count, severity mapping (High→error, Medium→warning), location structure, metadata properties (8 tests) - pip ExcludePatterns for virtual environment directories ## Checklist ### Required Checks * [x] Documentation is updated (if applicable) * [x] Files follow existing naming conventions * [x] Changes are backwards compatible (if applicable) * [x] Tests added for new functionality (if applicable) ### Required Automated Checks The following validation commands must pass before merging: * [x] Markdown linting: `npm run lint:md` * [x] Spell checking: `npm run spell-check` * [x] Frontmatter validation: `npm run lint:frontmatter` * [x] Skill structure validation: `npm run validate:skills` * [x] Link validation: `npm run lint:md-links` * [x] PowerShell analysis: `npm run lint:ps` * [x] Plugin freshness: `npm run plugin:generate` ## Security Considerations * [x] This PR does not contain any sensitive or NDA information * [x] Any new dependencies have been reviewed for security issues * [x] Security-related scripts follow the principle of least privilege ## Additional Notes This PR enforces npm dependency pinning using exact-version enforcement rather than SHA-pinning. The scanner uses regex-only analysis with no code execution, and `Test-Path -LiteralPath` guards all file reads. Test count increased from 93 to 100 with comprehensive SARIF integration coverage. --------- Signed-off-by: Marcel Bindseil <marcelbindseil@gmail.com> Co-authored-by: Bill Berry <wbery@microsoft.com> Co-authored-by: Marcel Bindseil <marcelbindseil@gmail.com> Co-authored-by: Jordan Knight <jakkaj@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vaughan Knight <vaughan@vaughanknight.com>
1 parent a2f18e3 commit 4e9e31f

21 files changed

+923
-295
lines changed

.cspell.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"Cargo.lock",
2424
"**/Cargo.lock",
2525
"CHANGELOG.md",
26-
"logs/**"
26+
"logs/**",
27+
"docs/docusaurus/build/**"
2728
],
2829
"ignoreRegExpList": [
2930
"/#.*/g",

.cspell/general-technical.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ containerd
131131
contravariance
132132
coredns
133133
corent
134+
coreutils
134135
corosync
135136
cortana
136137
couchbase
@@ -369,6 +370,7 @@ deserialize
369370
deserialized
370371
devopsdays
371372
devtest
373+
dgst
372374
diego
373375
differenced
374376
dimensionality
@@ -799,6 +801,7 @@ pulsant
799801
pulumi
800802
purescale
801803
pyodbc
804+
pypackages
802805
pypi
803806
pytest
804807
qgis
@@ -864,6 +867,7 @@ rescaling
864867
resiliency
865868
resourcegroups
866869
resourceless
870+
retargeted
867871
retraining
868872
reuse
869873
reverification

.github/instructions/workflows.instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ Avoid `format()` workarounds or environment variable indirection when the simple
312312

313313
The following scripts enforce compliance:
314314

315-
* `scripts/security/Test-DependencyPinning.ps1` - Validates SHA pinning
315+
* `scripts/security/Test-DependencyPinning.ps1` - Validates dependency pinning
316316
* `scripts/security/Test-SHAStaleness.ps1` - Checks for stale dependencies
317317
* `scripts/security/Test-WorkflowPermissions.ps1` - Validates workflow permissions declarations
318318
* `scripts/linting/Invoke-YamlLint.ps1` - Runs actionlint validation

.github/workflows/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Each modular workflow implements comprehensive 4-channel result publishing:
9898
9999
All workflows in this repository follow security best practices:
100100
101-
### SHA Pinning
101+
### Dependency Pinning
102102
103103
* All GitHub Actions use full 40-character commit SHAs
104104
* Comments include semantic version tags for human readability
@@ -126,7 +126,7 @@ The repository includes PowerShell scripts in `scripts/security/` for SHA pinnin
126126
* `Update-DockerSHAPinning.ps1` - Update Docker image SHA pins
127127
* `Update-ShellScriptSHAPinning.ps1` - Update shell script dependencies
128128
* `Test-SHAStaleness.ps1` - Check for stale SHA pins
129-
* `Test-DependencyPinning.ps1` - Validate SHA pinning compliance
129+
* `Test-DependencyPinning.ps1` - Validate dependency pinning compliance
130130

131131
### Dependabot Integration
132132

@@ -264,7 +264,7 @@ To add a new workflow to the repository:
264264

265265
1. Create `{tool-name}.yml` following existing patterns
266266
2. Implement 4-channel result publishing (annotations, artifacts, SARIF if security, summaries)
267-
3. Use SHA pinning for all actions
267+
3. Use dependency pinning for all dependencies
268268
4. Use minimal permissions
269269
5. Add soft-fail input support
270270
6. Update `pr-validation.yml` and `release-stable.yml` to include new job

.github/workflows/dependency-pinning-scan.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ on:
1212
description: 'Comma-separated list of dependency types to check'
1313
required: false
1414
type: string
15-
default: 'github-actions'
15+
default: 'github-actions,npm,workflow-npm-commands'
1616
soft-fail:
1717
description: 'Whether to continue on compliance violations'
1818
required: false
@@ -59,7 +59,7 @@ jobs:
5959
with:
6060
persist-credentials: false
6161

62-
- name: Run SHA Pinning Validation
62+
- name: Run Dependency Pinning Validation
6363
id: pinning
6464
shell: pwsh
6565
run: |
@@ -71,7 +71,6 @@ jobs:
7171
# Build parameter list for JSON output (always generate)
7272
$params = @{
7373
Path = '.'
74-
Recursive = $true
7574
Format = 'json'
7675
OutputPath = 'logs/dependency-pinning-results.json'
7776
}
@@ -165,7 +164,7 @@ jobs:
165164
166165
### ⚠️ Action Required
167166
168-
**$unpinnedCount dependencies are not SHA-pinned.**
167+
**$unpinnedCount dependencies are not properly pinned.**
169168
170169
Review the warnings in the workflow log and pin dependencies to specific SHA commits.
171170
@@ -175,7 +174,7 @@ jobs:
175174
176175
### ✅ All Dependencies Pinned
177176
178-
All dependencies are properly SHA-pinned.
177+
All dependencies are properly pinned.
179178
180179
"@
181180
})

.github/workflows/weekly-security-maintenance.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ permissions:
1919
contents: read
2020

2121
jobs:
22-
# Job 1: Validate all dependencies are SHA-pinned
22+
# Job 1: Validate dependency pinning
2323
validate-pinning:
24-
name: Validate SHA Pinning Compliance
24+
name: Validate Dependency Pinning Compliance
2525
uses: ./.github/workflows/dependency-pinning-scan.yml
2626
permissions:
2727
contents: read
@@ -81,7 +81,7 @@ jobs:
8181
8282
| Metric | Status | Details |
8383
|--------|--------|---------|
84-
| SHA Pinning Compliance | $(if ($unpinnedCount -eq '0') { '✅ Pass' } else { '⚠️ Warning' }) | Score: $pinningScore% |
84+
| Dependency Pinning Compliance | $(if ($unpinnedCount -eq '0') { '✅ Pass' } else { '⚠️ Warning' }) | Score: $pinningScore% |
8585
| Unpinned Dependencies | $(if ($unpinnedCount -eq '0') { '✅ None' } else { "⚠️ $unpinnedCount found" }) | $(if ($unpinnedCount -ne '0') { 'Review warnings above' } else { 'All pinned' }) |
8686
| Stale SHAs (>${{ inputs.max-age-days }} days) | $(if ($staleCount -eq '0') { '✅ None' } else { "⚠️ $staleCount found" }) | $(if ($staleCount -ne '0') { 'Review warnings above' } else { 'All current' }) |
8787
@@ -98,12 +98,12 @@ jobs:
9898
9999
## ⚠️ Action Required: Unpinned Dependencies
100100
101-
**$unpinnedCount dependencies are not SHA-pinned.** This is a security risk.
101+
**$unpinnedCount dependencies are not properly pinned.** This is a security risk.
102102
103103
**To fix:**
104104
``````powershell
105105
# Review the warnings above, then pin each dependency to a SHA
106-
# Example: Change 'uses: actions/checkout@v4' to 'uses: actions/checkout@71cf2267d89c5cb81562390fa70a37fa40b1305e # v4.2.2'
106+
# Example: Change 'actions/checkout@v4' to 'actions/checkout@<commit-sha> # v4.2.2'
107107
``````
108108
109109
"@
@@ -151,7 +151,7 @@ jobs:
151151
## ✅ All Clear!
152152
153153
Repository security posture is excellent:
154-
- All dependencies are SHA-pinned
154+
- All dependencies are properly pinned
155155
- All SHAs are current (< ${{ inputs.max-age-days || 30 }} days old)
156156
- CodeQL analysis completed successfully
157157
- GitHub Secret Scanning and Dependabot actively monitoring

.gitleaksignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# False positives: fake GitHub PAT 'ghp_test123456789' used as test fixture data
2-
# in Pester security test files for SHA pinning and staleness validation.
2+
# in Pester security test files for dependency pinning and staleness validation.
33
# Rule: generic-api-key
44

55
# Test-SHAStaleness.Tests.ps1

docs/architecture/workflows.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Individual validation workflows called by orchestration workflows:
7676
| `table-format.yml` | Markdown table formatting | `npm run format:tables` |
7777
| `pester-tests.yml` | PowerShell unit tests | `npm run test:ps` |
7878
| `skill-validation.yml` | Skill structure validation | `npm run validate:skills` |
79-
| `dependency-pinning-scan.yml` | GitHub Actions pinning | N/A (PowerShell direct) |
79+
| `dependency-pinning-scan.yml` | Dependency pinning validation | N/A (PowerShell direct) |
8080
| `sha-staleness-check.yml` | SHA reference freshness* | N/A (PowerShell direct) |
8181
| `codeql-analysis.yml` | CodeQL security scanning* | N/A (GitHub native) |
8282
| `dependency-review.yml` | Dependency vulnerability review* | N/A (GitHub native) |
@@ -135,7 +135,7 @@ flowchart LR
135135
| skill-validation | `skill-validation.yml` | Skill directory structure |
136136
| link-lang-check | `link-lang-check.yml` | Link accessibility |
137137
| markdown-link-check | `markdown-link-check.yml` | Broken links |
138-
| dependency-pinning-check | `dependency-pinning-scan.yml` | Action SHA pinning |
138+
| dependency-pinning-check | `dependency-pinning-scan.yml` | Dependency pinning |
139139
| npm-audit | Inline | npm dependency vulnerabilities |
140140
| codeql | `codeql-analysis.yml` | Code security patterns |
141141
| copyright-headers | `copyright-headers.yml` | Copyright header compliance |
@@ -200,23 +200,23 @@ When release-please creates a release, parallel jobs build the extension VSIX (`
200200

201201
The `weekly-security-maintenance.yml` workflow runs every Sunday at 2AM UTC, providing scheduled security posture review.
202202

203-
| Job | Purpose |
204-
|------------------|---------------------------------------|
205-
| validate-pinning | Verify GitHub Actions use SHA pinning |
206-
| check-staleness | Detect outdated SHA references |
207-
| codeql-analysis | Full CodeQL security scan |
208-
| summary | Aggregate security status report |
203+
| Job | Purpose |
204+
|------------------|--------------------------------------|
205+
| validate-pinning | Verify dependency pinning compliance |
206+
| check-staleness | Detect outdated SHA references |
207+
| codeql-analysis | Full CodeQL security scan |
208+
| summary | Aggregate security status report |
209209

210210
### Security Validation Tools
211211

212-
| Tool | Script | Checks |
213-
|--------------------|------------------------------|------------------------------------------|
214-
| Dependency Pinning | `Test-DependencyPinning.ps1` | Actions use SHA refs, not tags |
215-
| SHA Staleness | `Test-SHAStaleness.ps1` | SHAs reference recent commits |
216-
| npm Audit | `npm audit` | Known vulnerabilities in dependencies |
217-
| CodeQL | GitHub native | Code patterns indicating security issues |
218-
| Gitleaks | `gitleaks` | Secret detection in repository history |
219-
| Dependency Review | GitHub native | Dependency vulnerability analysis |
212+
| Tool | Script | Checks |
213+
|--------------------|------------------------------|-----------------------------------------------|
214+
| Dependency Pinning | `Test-DependencyPinning.ps1` | Actions use SHA refs; npm uses exact versions |
215+
| SHA Staleness | `Test-SHAStaleness.ps1` | SHAs reference recent commits |
216+
| npm Audit | `npm audit` | Known vulnerabilities in dependencies |
217+
| CodeQL | GitHub native | Code patterns indicating security issues |
218+
| Gitleaks | `gitleaks` | Secret detection in repository history |
219+
| Dependency Review | GitHub native | Dependency vulnerability analysis |
220220

221221
## Extension Publishing
222222

0 commit comments

Comments
 (0)