Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .github/actions/chef-download-grype-snapshot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Chef Download + Grype Snapshot Action

Composite action that downloads Chef products from downloads.chef.io and runs Grype vulnerability scans.

## Usage

```yaml
- name: Scan chef product
uses: chef/common-github-actions/.github/actions/chef-download-grype-snapshot@main
with:
product: chef
channel: stable
download_site: commercial
os: ubuntu
os_version: "24.04"
arch: x86_64
scan_mode: native
scan_root: /opt/chef
license_id: ${{ secrets.LICENSE_ID }}
```

## Inputs

| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `product` | Yes | - | Chef product name (chef, chef-workstation, chef-server, etc.) |
| `channel` | Yes | - | Release channel (stable, current) |
| `download_site` | Yes | commercial | Download site (commercial or community) |
| `os` | Yes | ubuntu | OS platform |
| `os_version` | Yes | - | OS version (e.g., 24.04) |
| `arch` | Yes | x86_64 | Architecture |
| `scan_mode` | Yes | native | Scan mode (native or habitat) |
| `scan_root` | Yes | - | Install root path for metadata (e.g., /opt/chef) |
| `resolve_version` | Yes | latest | Version resolution (latest or pinned) |
| `pinned_version` | No | "" | Specific version when resolve_version=pinned |
| `license_id` | No | "" | License ID for downloads (pass via secrets) |
| `out_dir` | No | out | Output directory for results |
| `work_dir` | No | work | Working directory for temporary files |

## Outputs

| Output | Description |
|--------|-------------|
| `resolved_version` | The resolved product version that was scanned |
| `download_url_redacted` | Download URL with license_id removed |

## Output Files

The action generates two JSON files in the `out_dir`:

- **latest.json**: Complete Grype scan results
- **metadata.json**: Scan metadata including version, environment, and severity counts

## Requirements

- Ubuntu runner (uses `dpkg` for package extraction)
- Grype is automatically installed if not present
- Valid license_id for the specified download_site:
- Commercial sites require a commercial license
- Community sites require a Free license

## Download Site Constraints

- **Commercial**: Supports both `stable` and `current` channels
- **Community**: Only supports `stable` channel (API enforced)

## Error Handling

The action provides detailed error messages for common failures:
- Missing or expired license_id
- Wrong license type (commercial vs Free)
- Invalid product/channel combinations
- Package download failures

## Example with Multiple Products

```yaml
jobs:
scan:
runs-on: ubuntu-latest
strategy:
matrix:
product: [chef, chef-workstation, chef-server]
channel: [stable, current]
steps:
- uses: chef/common-github-actions/.github/actions/chef-download-grype-snapshot@main
with:
product: ${{ matrix.product }}
channel: ${{ matrix.channel }}
download_site: commercial
os: ubuntu
os_version: "24.04"
arch: x86_64
scan_root: /opt/${{ matrix.product }}
license_id: ${{ secrets.GA_DOWNLOAD_GRYPE_LICENSE_ID }}

- name: Upload results
uses: actions/upload-artifact@v4
with:
name: scan-${{ matrix.product }}-${{ matrix.channel }}
path: out/
```

## Related Projects

- [chef-vuln-scan-orchestrator](https://github.com/chef/chef-vuln-scan-orchestrator) - Orchestration workflow using this action
- [chef-vuln-scan-data](https://github.com/chef/chef-vuln-scan-data) - Data repository for scan results
91 changes: 91 additions & 0 deletions .github/actions/chef-download-grype-snapshot/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: "Chef Download + Grype Snapshot"
description: "Resolve latest version, download package, extract, run Grype JSON scan, and generate metadata.json"
inputs:
product:
required: true
description: "Chef product (chef, chef-workstation, chef-server, etc.)"
channel:
required: true
description: "Release channel (stable, current)"
download_site:
required: true
description: "commercial or community"
default: "commercial"
os:
required: true
description: "OS platform (ubuntu)"
default: "ubuntu"
os_version:
required: true
description: "OS version (e.g., 24.04)"
arch:
required: true
description: "Architecture (x86_64)"
default: "x86_64"
scan_mode:
required: true
description: "native|habitat (native for pilot)"
default: "native"
scan_root:
required: true
description: "Expected install root (for metadata; e.g., /opt/chef)"
resolve_version:
required: true
description: "latest|pinned"
default: "latest"
pinned_version:
required: false
description: "Version to scan when resolve_version=pinned"
default: ""
license_id:
required: false
description: "License ID for commercial downloads (pass via secrets)"
default: ""
out_dir:
required: false
description: "Output directory"
default: "out"
work_dir:
required: false
description: "Working directory"
default: "work"

outputs:
resolved_version:
description: "Resolved version string"
value: ${{ steps.run.outputs.resolved_version }}
download_url_redacted:
description: "Download URL with license_id stripped"
value: ${{ steps.run.outputs.download_url_redacted }}

runs:
using: "composite"
steps:
- name: Run snapshot logic
id: run
shell: bash
env:
PRODUCT: ${{ inputs.product }}
CHANNEL: ${{ inputs.channel }}
DOWNLOAD_SITE: ${{ inputs.download_site }}
OS: ${{ inputs.os }}
OS_VERSION: ${{ inputs.os_version }}
ARCH: ${{ inputs.arch }}
SCAN_MODE: ${{ inputs.scan_mode }}
SCAN_ROOT: ${{ inputs.scan_root }}
RESOLVE_VERSION: ${{ inputs.resolve_version }}
PINNED_VERSION: ${{ inputs.pinned_version }}
LICENSE_ID: ${{ inputs.license_id }}
OUT_DIR: ${{ inputs.out_dir }}
WORK_DIR: ${{ inputs.work_dir }}
run: |
set -euo pipefail
if [ -n "${LICENSE_ID:-}" ]; then
echo "::add-mask::${LICENSE_ID}"
fi

python "${GITHUB_ACTION_PATH}/run.py"

# expose outputs for calling workflow
echo "resolved_version=$(cat ${OUT_DIR}/_resolved_version.txt)" >> "$GITHUB_OUTPUT"
echo "download_url_redacted=$(cat ${OUT_DIR}/_download_url_redacted.txt)" >> "$GITHUB_OUTPUT"
Loading