-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #40 from launchdarkly/behn/IDE-761/clone-dependenc…
…y-scan-github Clone dependency-scan action to public actions repo
- Loading branch information
Showing
7 changed files
with
303 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
.idea | ||
.vscode | ||
|
||
**/.DS_Store | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# dependency-scan | ||
|
||
This set of actions is used for license validation to prevent GPL licenses from being shipped in our product. | ||
|
||
There are four sub-actions in this action. | ||
|
||
## [generate-sbom](./generate-sbom) | ||
|
||
This action generates a Software Bill of Materials (SBOM) for a project. It is uploaded as an artifact to the | ||
GitHub Action workspace. | ||
|
||
## [evaluate-policy](./evaluate-policy) | ||
|
||
This action evaluates the SBOM against the policy file. A summary is posted to GitHub Actions. | ||
|
||
# Example workflows | ||
|
||
## Serial generation of BOMs | ||
|
||
```yaml | ||
name: Workflow | ||
on: pull_request | ||
|
||
jobs: | ||
dependency-scan: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Setup Go | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version-file: go.mod | ||
- name: Generate SBOM | ||
uses: launchdarkly/gh-actions/actions/dependency-scan/generate-sbom@main | ||
with: | ||
types: 'go,nodejs' | ||
- name: Evaluate SBOM Policy | ||
uses: launchdarkly/gh-actions/actions/dependency-scan/evaluate-policy@main | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<!-- action-docs-description source="action.yml" --> | ||
## Description | ||
|
||
Evaluate the OPA license policy against the BOM | ||
<!-- action-docs-description source="action.yml" --> | ||
|
||
<!-- action-docs-inputs source="action.yml" --> | ||
## Inputs | ||
|
||
| name | description | required | default | | ||
| --- | --- | --- | --- | | ||
| `policy-file` | <p>Filename of the license policy.</p> | `false` | `license_policy.rego` | | ||
| `bom-file` | <p>File name for the Bill of Materials file</p> | `false` | `bom.json` | | ||
| `opa-query` | <p>OPA query to use. If anything is returned, the build will fail.</p> | `false` | `data.launchdarkly.violation[x]` | | ||
| `opa-version` | <p>The Open Policy Agent version to use.</p> | `false` | `latest` | | ||
| `cyclonedx-version` | <p>The CycloneDX version to use.</p> | `false` | `latest` | | ||
| `artifacts-pattern` | <p>Download one or more artifacts and merge them, prior to validation.</p> | `false` | `""` | | ||
<!-- action-docs-inputs source="action.yml" --> | ||
|
||
<!-- action-docs-outputs source="action.yml" --> | ||
|
||
<!-- action-docs-outputs source="action.yml" --> | ||
|
||
<!-- action-docs-runs source="action.yml" --> | ||
## Runs | ||
|
||
This action is a `composite` action. | ||
<!-- action-docs-runs source="action.yml" --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
name: Evaluate Policy | ||
description: Evaluate the OPA license policy against the BOM | ||
|
||
inputs: | ||
policy-file: | ||
description: Filename of the license policy. | ||
required: false | ||
default: license_policy.rego | ||
bom-file: | ||
description: File name for the Bill of Materials file | ||
required: false | ||
default: bom.json | ||
opa-query: | ||
description: OPA query to use. If anything is returned, the build will fail. | ||
required: false | ||
default: data.launchdarkly.violation[x] | ||
opa-version: | ||
description: The Open Policy Agent version to use. | ||
required: false | ||
default: latest | ||
cyclonedx-version: | ||
description: The CycloneDX version to use. | ||
required: false | ||
default: latest | ||
artifacts-pattern: | ||
description: Download one or more artifacts and merge them, prior to validation. | ||
required: false | ||
|
||
runs: | ||
using: composite | ||
steps: | ||
- name: Open Policy Agent CLI version | ||
id: versions | ||
shell: bash | ||
run: | | ||
if [[ ${{ inputs.opa-version }} == latest ]]; then | ||
echo "opa=$(curl -s https://api.github.com/repos/open-policy-agent/opa/releases/latest | jq -r .tag_name)" >> $GITHUB_OUTPUT | ||
else | ||
echo "opa=${{ inputs.opa-version }}" >> $GITHUB_OUTPUT | ||
fi | ||
if [[ "${{ inputs.artifacts-pattern }}" != "" && ${{ inputs.cyclonedx-version }} == latest ]]; then | ||
echo "cyclonedx=$(curl -s https://api.github.com/repos/CycloneDX/cyclonedx-cli/releases/latest | jq -r .tag_name)" >> $GITHUB_OUTPUT | ||
else | ||
echo "cyclonedx=${{ inputs.cyclonedx-version }}" >> $GITHUB_OUTPUT | ||
fi | ||
- name: Cache Open Policy Agent | ||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 | ||
id: cache-opa | ||
with: | ||
path: /usr/local/bin/opa | ||
key: opa-${{ steps.versions.outputs.opa }} | ||
|
||
- name: Install Open Policy Agent | ||
if: steps.cache-opa.outputs.cache-hit != 'true' | ||
shell: bash | ||
run: | | ||
curl -L https://openpolicyagent.org/downloads/${{ steps.versions.outputs.opa }}/opa_linux_amd64 -o /usr/local/bin/opa | ||
chmod +x /usr/local/bin/opa | ||
- name: Cache CycloneDX CLI tool | ||
if: inputs.artifacts-pattern != '' | ||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 | ||
id: cyclonedx-opa | ||
with: | ||
path: /usr/local/bin/cyclonedx | ||
key: cyclonedx-${{ steps.versions.outputs.cyclonedx }} | ||
|
||
- name: Install CycloneDX CLI tool | ||
if: inputs.artifacts-pattern != '' && steps.cyclonedx-opa.outputs.cache-hit != 'true' | ||
shell: bash | ||
run: | | ||
curl -L https://github.com/CycloneDX/cyclonedx-cli/releases/download/${{ steps.versions.outputs.cyclonedx }}/cyclonedx-linux-x64 -o /usr/local/bin/cyclonedx | ||
chmod +x /usr/local/bin/cyclonedx | ||
- name: Get all BOMs | ||
if: inputs.artifacts-pattern != '' | ||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 | ||
with: | ||
path: . | ||
pattern: ${{ inputs.artifacts-pattern }} | ||
|
||
- name: Merge BOMs | ||
if: inputs.artifacts-pattern != '' | ||
run: cyclonedx merge --input-files ${{ inputs.artifacts-pattern }}/*.json --output-file ${{ inputs.bom-file }} | ||
shell: bash | ||
|
||
- name: Store Bill of Materials | ||
if: inputs.artifacts-pattern != '' | ||
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 | ||
with: | ||
name: merged-bom | ||
path: ${{ inputs.bom-file }} | ||
retention-days: 90 | ||
|
||
- name: Evaluate Policy | ||
run: | | ||
json_data="$(opa eval --fail-defined -i ${{ inputs.bom-file }} -d $GITHUB_ACTION_PATH/${{ inputs.policy-file }} '${{ inputs.opa-query }}' --format json)" || { | ||
rc=$? | ||
{ | ||
echo "Status: :x: failed" | ||
echo "" | ||
jq <<<"$json_data" -r ' | ||
[.result[].expressions[].value] | # Filter down to what we want | ||
(.[0] | to_entries | map(.key)), # First element keys as header row | ||
["---", "---"], # Markdown table header identifier | ||
(.[] | to_entries | map(.value)) | # Finally get the real values | ||
join(" | ") # join by "|" (to mark columns) | ||
' | ||
} >> $GITHUB_STEP_SUMMARY | ||
printf "%s\n" "$json_data" | ||
exit $rc | ||
} | ||
echo "Status: :white_check_mark: success" >> $GITHUB_STEP_SUMMARY | ||
shell: bash |
21 changes: 21 additions & 0 deletions
21
actions/dependency-scan/evaluate-policy/license_policy.rego
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package launchdarkly | ||
|
||
default allow = false # unless otherwise defined, allow is false | ||
|
||
allow = true { # allow is true if... | ||
count(violation) == 0 # there are zero violations. | ||
} | ||
|
||
# The golang cyclonedx tool puts licenses under 'evidence' | ||
violation[component["bom-ref"]] = {"dependency": component["bom-ref"], "license": license} { | ||
component := input.components[_] | ||
license := component.evidence.licenses[_].license.id | ||
contains(license, "GPL") # should catch GPL, LGPL, AGPL, etc | ||
} | ||
|
||
# The Node cyclonedx tool puts licenses directly under the component | ||
violation[component["bom-ref"]] = {"dependency": component["bom-ref"], "license": license} { | ||
component := input.components[_] | ||
license := component.licenses[_].license.id | ||
contains(license, "GPL") # should catch GPL, LGPL, AGPL, etc | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<!-- action-docs-description source="action.yml" --> | ||
## Description | ||
|
||
Generate a Software Bill of Materials (SBOM) | ||
<!-- action-docs-description source="action.yml" --> | ||
|
||
<!-- action-docs-inputs source="action.yml" --> | ||
## Inputs | ||
|
||
| name | description | required | default | | ||
| --- | --- | --- | --- | | ||
| `types` | <p>Comma separated project types. Please refer to https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES for supported languages/platforms.</p> | `true` | `""` | | ||
| `cdxgen-version` | <p>Version of cdxgen to use</p> | `false` | `10.9.2` | | ||
| `project-directory` | <p>Relative path (from root of repo) to the root of the golang project / module</p> | `false` | `.` | | ||
| `fetch-license` | <p>Fetch license information for dependencies</p> | `false` | `true` | | ||
| `recurse` | <p>Recurse mode suitable for mono-repos.</p> | `false` | `true` | | ||
<!-- action-docs-inputs source="action.yml" --> | ||
|
||
<!-- action-docs-outputs source="action.yml" --> | ||
|
||
<!-- action-docs-outputs source="action.yml" --> | ||
|
||
<!-- action-docs-runs source="action.yml" --> | ||
## Runs | ||
|
||
This action is a `composite` action. | ||
<!-- action-docs-runs source="action.yml" --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
name: Generate SBOM | ||
description: Generate a Software Bill of Materials (SBOM) | ||
|
||
inputs: | ||
types: | ||
description: 'Comma separated project types. Please refer to https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES for supported languages/platforms.' | ||
required: true | ||
cdxgen-version: | ||
description: 'Version of cdxgen to use' | ||
required: false | ||
default: '10.9.2' | ||
project-directory: | ||
description: 'Relative path (from root of repo) to the root of the golang project / module' | ||
required: false | ||
default: '.' | ||
fetch-license: | ||
description: 'Fetch license information for dependencies' | ||
required: false | ||
default: 'true' | ||
recurse: | ||
description: 'Recurse mode suitable for mono-repos.' | ||
required: false | ||
default: 'true' | ||
|
||
runs: | ||
using: composite | ||
|
||
steps: | ||
- name: Setup Node | ||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 | ||
with: | ||
node-version: 20.x | ||
|
||
- name: Install cdxgen | ||
run: npm list -g | grep -qF @cyclonedx/cdxgen@${{ inputs.cdxgen-version }} || npm install -g @cyclonedx/cdxgen@${{ inputs.cdxgen-version }} | ||
shell: bash | ||
|
||
- name: Get information | ||
id: info | ||
shell: bash | ||
run: | | ||
if [[ "${{ inputs.types }}" == *,* ]]; then | ||
echo "artifact_name=bom" >> "$GITHUB_OUTPUT" | ||
echo "bom_file=bom.json" >> "$GITHUB_OUTPUT" | ||
else | ||
echo "artifact_name=bom-${{ inputs.types }}" >> "$GITHUB_OUTPUT" | ||
echo "bom_file=bom.${{ inputs.types }}.json" >> "$GITHUB_OUTPUT" | ||
fi | ||
- name: Generate Bill of Materials | ||
shell: bash | ||
run: | | ||
IFS=, read -ra types <<<"${{ inputs.types }}" | ||
args=() | ||
for type in "${types[@]}"; do | ||
args+=(-t "$type") | ||
done | ||
[[ "${{ inputs.recurse }}" != true ]] && args+=(--no-recurse) | ||
cdxgen "${args[@]}" -o "${{ steps.info.outputs.bom_file }}" ${{ inputs.project-directory }} | ||
env: | ||
FETCH_LICENSE: ${{ inputs.fetch-license }} | ||
|
||
- name: Store Bill of Materials | ||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 | ||
with: | ||
name: ${{ steps.info.outputs.artifact_name }} | ||
path: ${{ steps.info.outputs.bom_file }} | ||
retention-days: 1 |