Skip to content

Commit c52d6e2

Browse files
fix(security): add artifact attestation for signed releases (#257)
## Description Add GitHub Artifact Attestations to the release workflow, enabling cryptographic signing of VSIX release artifacts using Sigstore. This satisfies the OSSF Best Practices `signed_releases` criterion by establishing verifiable build provenance. - Added `attest-and-upload` job to `main.yml` that downloads the VSIX artifact, attests it with Sigstore, and uploads it to the GitHub Release - Configured OIDC permissions (`id-token: write`, `attestations: write`, `contents: write`) for Sigstore integration - Documented verification process in SECURITY.md with GitHub CLI commands ## Related Issue(s) Related to OSSF Silver badge requirements for signed releases. ## Type of Change Select all that apply: **Code & Documentation:** - [ ] Bug fix (non-breaking change fixing an issue) - [ ] New feature (non-breaking change adding functionality) - [ ] Breaking change (fix or feature causing existing functionality to change) - [x] Documentation update **Infrastructure & 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`) > **Note for AI Artifact Contributors**: > > - **Agents**: Research, indexing/referencing other project (using standard VS Code GitHub Copilot/MCP tools), planning, and general implementation agents likely already exist. Review `.github/agents/` before creating new ones. > - **Model Versions**: Only contributions targeting the **latest Anthropic and OpenAI models** will be accepted. Older model versions (e.g., GPT-3.5, Claude 3) will be rejected. > - See [Agents Not Accepted](../docs/contributing/custom-agents.md#agents-not-accepted) and [Model Version Requirements](../docs/contributing/ai-artifacts-common.md#model-version-requirements). **Other:** - [ ] Script/automation (`.ps1`, `.sh`, `.py`) - [ ] Other (please describe): ## Sample Prompts (for AI Artifact Contributions) N/A - This PR does not include AI artifact contributions. ## Testing - Validated workflow YAML syntax - Verified action SHA pinning follows repository conventions - Ran `npm run lint:md` and `npm run lint:frontmatter` (passed) ## Checklist ### Required Checks - [x] Documentation is updated (if applicable) - [x] Files follow existing naming conventions - [x] Changes are backwards compatible (if applicable) ### AI Artifact Contributions N/A ### Required Automated Checks The following validation commands must pass before merging: - [x] Markdown linting: `npm run lint:md` - [ ] Spell checking: `npm run spell-check` - [x] Frontmatter validation: `npm run lint:frontmatter` - [ ] Link validation: `npm run lint:md-links` - [ ] PowerShell analysis: `npm run lint:ps` ## Security Considerations - [x] This PR does not contain any sensitive or NDA information - [ ] Any new dependencies have been reviewed for security issues - [x] Security-related scripts follow the principle of least privilege ## Additional Notes The `attest-and-upload` job runs only when `release_created == 'true'`, ensuring attestation occurs only for actual releases. Actions use SHA pinning per repository conventions: - `actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16` (v4.1.8) - `actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2` (v2.2.3) 🔒 - Generated by Copilot
1 parent ae76cab commit c52d6e2

File tree

4 files changed

+105
-34
lines changed

4 files changed

+105
-34
lines changed

.github/workflows/main.yml

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,28 +58,13 @@ jobs:
5858
changed-files-only: false
5959
code-coverage: true
6060

61-
extension-package:
62-
name: Package VS Code Extension
63-
needs:
64-
- spell-check
65-
- markdown-lint
66-
- table-format
67-
- dependency-pinning-scan
68-
- pester-tests
69-
uses: ./.github/workflows/extension-package.yml
70-
with:
71-
dev-patch-number: ${{ github.run_number }}
72-
permissions:
73-
contents: read
74-
7561
release-please:
7662
name: Release Please
7763
needs:
7864
- spell-check
7965
- markdown-lint
8066
- table-format
8167
- dependency-pinning-scan
82-
- extension-package
8368
- pester-tests
8469
runs-on: ubuntu-latest
8570
outputs:
@@ -106,3 +91,45 @@ jobs:
10691
token: ${{ steps.app-token.outputs.token }}
10792
config-file: release-please-config.json
10893
manifest-file: .release-please-manifest.json
94+
95+
extension-package-release:
96+
name: Package VS Code Extension (Release)
97+
needs: [release-please]
98+
if: ${{ needs.release-please.outputs.release_created == 'true' }}
99+
uses: ./.github/workflows/extension-package.yml
100+
with:
101+
version: ${{ needs.release-please.outputs.version }}
102+
permissions:
103+
contents: read
104+
105+
attest-and-upload:
106+
name: Attest and Upload Release Assets
107+
needs: [release-please, extension-package-release]
108+
if: ${{ needs.release-please.outputs.release_created == 'true' }}
109+
runs-on: ubuntu-latest
110+
permissions:
111+
contents: write
112+
id-token: write
113+
attestations: write
114+
steps:
115+
- name: Download VSIX artifact
116+
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
117+
with:
118+
name: extension-vsix
119+
path: ./dist
120+
121+
- name: Attest build provenance
122+
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
123+
with:
124+
subject-path: 'dist/*.vsix'
125+
126+
- name: Upload VSIX to GitHub Release
127+
env:
128+
GH_TOKEN: ${{ github.token }}
129+
run: |
130+
VSIX_FILE=$(find dist -name '*.vsix' | head -1)
131+
if [ -z "$VSIX_FILE" ]; then
132+
echo "::error::No VSIX file found in dist/"
133+
exit 1
134+
fi
135+
gh release upload "${{ needs.release-please.outputs.tag_name }}" "$VSIX_FILE" --clobber -R "${{ github.repository }}"

SECURITY.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,48 @@ Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https:
5353

5454
<!-- END MICROSOFT SECURITY.MD BLOCK -->
5555

56+
## Verifying Release Integrity
57+
58+
HVE Core releases are cryptographically signed using GitHub Artifact Attestations. This establishes provenance and allows you to verify that release artifacts were built from this repository's official CI/CD pipeline.
59+
60+
### Verification Steps
61+
62+
1. Install the GitHub CLI if not already available:
63+
64+
```bash
65+
# Windows (winget)
66+
winget install GitHub.cli
67+
68+
# macOS (Homebrew)
69+
brew install gh
70+
```
71+
72+
2. Download the release artifact (replace `<version>` with the release tag, e.g., `v1.2.0`):
73+
74+
```bash
75+
gh release download <version> -R microsoft/hve-core -p '*.vsix'
76+
```
77+
78+
3. Verify the attestation:
79+
80+
```bash
81+
gh attestation verify hve-core-<version>.vsix -R microsoft/hve-core
82+
```
83+
84+
A successful verification confirms:
85+
86+
* The artifact was built from the microsoft/hve-core repository
87+
* The build occurred in GitHub Actions
88+
* The artifact has not been modified since signing
89+
90+
### What Gets Signed
91+
92+
| Artifact | Channel | Signed |
93+
|------------------------|-----------------|-----------------------|
94+
| VSIX extension package | GitHub Releases | Yes |
95+
| VS Code Marketplace | Stable | Marketplace signature |
96+
| VS Code Marketplace | Pre-Release | Marketplace signature |
97+
5698
---
5799

58100
🤖 Crafted with precision by ✨Copilot following brilliant human instruction, then carefully refined by our team of discerning human reviewers.

docs/architecture/workflows.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -126,29 +126,29 @@ The `main.yml` workflow runs after merges to main, performing validation and rel
126126

127127
```mermaid
128128
flowchart LR
129-
V1[spell-check] --> PKG[extension-package]
130-
V2[markdown-lint] --> PKG
131-
V3[table-format] --> PKG
132-
V4[dependency-pinning-scan] --> PKG
133-
V1 --> RP[release-please]
134-
V2 --> RP
135-
V3 --> RP
136-
V4 --> RP
137-
PKG --> RP
129+
V1[spell-check] --> RP[release-please]
130+
V2[markdown-lint] --> RP
131+
V3[table-format] --> RP
132+
V4[dependency-pinning-scan] --> RP
133+
V5[pester-tests] --> RP
134+
RP -->|release_created| PKG[extension-package-release]
135+
PKG --> ATT[attest-and-upload]
138136
```
139137

140138
### Main Branch Jobs
141139

142-
| Job | Purpose | Dependencies |
143-
|-------------------------|--------------------------------|---------------------|
144-
| spell-check | Post-merge spelling validation | None |
145-
| markdown-lint | Post-merge markdown validation | None |
146-
| table-format | Post-merge table validation | None |
147-
| dependency-pinning-scan | Security pinning check | None |
148-
| extension-package | Build VS Code extension VSIX | All validation jobs |
149-
| release-please | Automated release management | All jobs |
150-
151-
The release-please job creates release PRs and tags based on conventional commits.
140+
| Job | Purpose | Dependencies |
141+
|---------------------------|--------------------------------|------------------------------|
142+
| spell-check | Post-merge spelling validation | None |
143+
| markdown-lint | Post-merge markdown validation | None |
144+
| table-format | Post-merge table validation | None |
145+
| dependency-pinning-scan | Security pinning check | None |
146+
| pester-tests | PowerShell unit tests | None |
147+
| release-please | Automated release management | All validation jobs |
148+
| extension-package-release | Build release VSIX | release-please (conditional) |
149+
| attest-and-upload | Sign and upload VSIX | extension-package-release |
150+
151+
When release-please creates a release, the `extension-package-release` job builds the VSIX with the correct version, and `attest-and-upload` signs it with Sigstore attestation before uploading to the GitHub Release.
152152

153153
## Security Workflows
154154

scripts/security/Update-ActionSHAPinning.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,10 @@ $ActionSHAMap = @{
242242
"actions/cache@v3" = "actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8" # v3.3.1
243243
"actions/upload-artifact@v4" = "actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808" # v4.3.6
244244
"actions/upload-artifact@v3" = "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" # v3.1.3
245+
"actions/download-artifact@v7" = "actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131" # v7.0.0
245246
"actions/download-artifact@v4" = "actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16" # v4.1.8
246247
"actions/download-artifact@v3" = "actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a" # v3.0.2
248+
"actions/attest-build-provenance@v2" = "actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2" # v2.2.3
247249
"github/super-linter@v6" = "github/super-linter@4ac6c1e9bce95c4e5e456c8c2c6b468998248097" # v6.8.0
248250
"github/super-linter@v5" = "github/super-linter@45fc0d88288beee4701c62761281edfee85655d7" # v5.7.2
249251
"hashicorp/setup-terraform@v3" = "hashicorp/setup-terraform@651471c36a6092792c552e8b1bef71e592b462d8" # v3.1.1

0 commit comments

Comments
 (0)