Skip to content
Open
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
25 changes: 25 additions & 0 deletions .github/actions/scorecard-action/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: 'Scorecard Scan'
description: 'Run OSSF scorecard'
inputs:
gh_token:
description: 'GitHub token for authentication'
required: true
outputs:
results_file_path:
description: 'Absolute path to the SARIF results file'
value: ${{ steps.set_sarif_file_path_output.outputs.sarif_file_path }}
runs:
using: 'composite'
steps:
- name: "Run scorecard"
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a
with:
results_file: results.sarif
results_format: sarif
publish_results: false
env:
GH_TOKEN: ${{ inputs.gh_token }}
- name: "Set SARIF file path output"
id: set_sarif_file_path_output
run: echo "sarif_file_path=${{ github.workspace }}/results.sarif" >> $GITHUB_OUTPUT
shell: bash
72 changes: 72 additions & 0 deletions .github/actions/semgrep-action/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: 'Semgrep Scan'
description: 'Run Semgrep scan with custom rules and output results'
inputs:
results_format:
description: 'Format of the results file (json or sarif)'
required: false
default: 'json'
show_results_in_pr:
description: 'Show results in PR (true/false)'
required: false
default: 'true'
outputs:
results_file_path:
description: 'Absolute path to the Semgrep results file'
value: ${{ steps.set_results_file_path_output.outputs.results_file_path }}
runs:
using: 'composite'
steps:
- name: "Fetch semgrep rules"
run: |
git clone https://github.com/trailofbits/semgrep-rules $HOME/semgrep-rules-tob
git clone https://github.com/semgrep/semgrep-rules $HOME/semgrep-rules
git -C $HOME/semgrep-rules reset --hard 518f71b883d431fa33268844b066033507e7c1b5
git -C $HOME/semgrep-rules-tob reset --hard 3b91c9b622b4a250b144a832ce73091b1f25e207
rm $HOME/semgrep-rules-tob/.github/workflows/update-semgrep-registry.yml
rm $HOME/semgrep-rules/.pre-commit-config.yaml
rm -rf $HOME/semgrep-rules-tob/.github
rm -rf $HOME/semgrep-rules/.github
rm -rf $HOME/semgrep-rules/stats
shell: bash
- name: "Setup git repo"
run: git config --global --add safe.directory $(pwd)
shell: bash
- name: "Run semgrep"
run: |
results_file="/tmp/semgrep-results.${{ inputs.results_format }}"
baseline_commit_arg=""
if [ "${{ inputs.show_results_in_pr }}" = "true" ]; then
baseline_commit_arg="--baseline-commit=${{ github.event.pull_request.base.sha }}"
fi
semgrep scan --config $HOME/semgrep-rules --config $HOME/semgrep-rules-tob \
--metrics=off --experimental \
--exclude-rule=third-party-action-not-pinned-to-commit-sha \
--exclude-rule=jsx-not-internationalized \
--severity=WARNING \
--severity=ERROR \
--exclude="*.html" --exclude="*.js" --exclude="*.spec.ts" \
$baseline_commit_arg \
--${{ inputs.results_format }} | \
jq 'to_entries | map(if .key == "results" then .value |= map(select(.extra.metadata.confidence != "LOW")) else . end) | from_entries' \
> "$results_file" || true

Check failure on line 51 in .github/actions/semgrep-action/action.yml

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

run-shell-injection

Using variable interpolation ... with github context data in a run step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead use an intermediate environment variable with env to store the data and use the environment variable in the run script. Be sure to use doublequotes the environment variable like this ENVVAR.
shell: bash
- name: "Set results file path output"
id: set_results_file_path_output
run: echo "results_file_path=/tmp/semgrep-results.${{ inputs.results_format }}" >> $GITHUB_OUTPUT

Check failure on line 55 in .github/actions/semgrep-action/action.yml

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

run-shell-injection

Using variable interpolation ... with github context data in a run step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead use an intermediate environment variable with env to store the data and use the environment variable in the run script. Be sure to use doublequotes the environment variable like this ENVVAR.
shell: bash
- name: "Show results in PR"
if: ${{ inputs.show_results_in_pr == 'true' && inputs.results_format == 'json' }}
run: |
/usr/bin/jq -r '.results[]
| . + {
severity: (
if .extra.severity == "WARNING" then "warning"
elif .extra.severity == "ERROR" then "error"
else "notice"
end
)
}
| "::\(.severity) file=\(.path),line=\(.start.line),col=\(.start.col),endLine=\(.end.line),endColumn=\(.end.col),title=\(.check_id)::\((.extra.message
| gsub("[^a-zA-Z0-9 .]"; "")
))"' /tmp/semgrep-results.json
shell: bash
77 changes: 77 additions & 0 deletions .github/actions/upload-defectdojo/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: 'Upload results to DefectDojo'
description: 'Uploads a scan report to DefectDojo, creating the product if needed.'
author: 'GitHub Copilot'
runs:
using: 'composite'
steps:
- name: Run upload script inline
run: |
python3 <<EOF
import os
import sys
import requests

def main():
base_url = 'https://defectdojo-api.corp.zoo.dev'
dd_token = os.environ['INPUT_DD_TOKEN']
repo_name = os.environ['GITHUB_REPOSITORY'].split('/')[-1]
report_path = os.environ['INPUT_REPORT_PATH']
scan_type = os.environ['INPUT_SCAN_TYPE']
engagement = os.environ['INPUT_ENGAGEMENT']

# Search for product
params = {'name_exact': repo_name}
url = f'{base_url}/api/v2/products/'
headers = {'Authorization': f'Token {dd_token}'}
resp = requests.get(url, params=params, headers=headers)
if not resp.ok:
print(f'Failed to search for product: {resp.status_code} {resp.text}')
sys.exit(1)
products = resp.json()
results = products.get('results', [])
if len(results) > 1:
print(f'Found multiple products for repository {repo_name}. Please ensure the product name is unique.')
sys.exit(1)
elif len(results) == 0:
print(f'No product found for repository {repo_name}. Please create the product in DefectDojo first.')
sys.exit(1)

# Upload scan
upload_url = f'{base_url}/api/v2/reimport-scan/'
data = {
'product_name': repo_name,
'engagement_name': engagement,
'scan_type': scan_type,
'auto_create_context': 'True',
}
with open(report_path, 'rb') as f:
files = {'file': (os.path.basename(report_path), f, 'application/octet-stream')}
resp = requests.post(upload_url, data=data, files=files, headers=headers)
if not resp.ok:
print(f'Failed to reimport scan: {resp.status_code} {resp.text}')
sys.exit(1)
print(resp.text)

if __name__ == '__main__':
main()
EOF
env:
GITHUB_REPOSITORY: ${{ github.repository }}
INPUT_DD_TOKEN: ${{ inputs.dd_token }}
INPUT_REPORT_PATH: ${{ inputs.report_path }}
INPUT_SCAN_TYPE: ${{ inputs.scan_type }}
INPUT_ENGAGEMENT: ${{ inputs.engagement }}
shell: bash
inputs:
dd_token:
description: 'DefectDojo API token'
required: true
report_path:
description: 'Path to the report file to upload'
required: true
scan_type:
description: 'Scan type for DefectDojo'
required: true
engagement:
description: 'Engagement name in DefectDojo'
required: true
35 changes: 35 additions & 0 deletions .github/actions/zizmor-action/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: 'Zizmor Scan'
description: 'Install uv and run zizmor scan with custom config'
inputs:
gh_token:
description: 'GitHub token for authentication'
required: true
results_format:
description: 'Format of the results file (github, json, sarif)'
required: false
default: 'github'
outputs:
results_file_path:
description: 'Absolute path to the Zizmor results file'
value: ${{ steps.set_results_file_path_output.outputs.results_file_path }}
runs:
using: 'composite'
steps:
- name: Install the latest version of uv
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41
- name: Run zizmor 🌈
run: |
cat <<EOF > zizmor.yml
rules:
unpinned-uses:
disable: true
EOF
results_file="/tmp/zizmor-results.${{ inputs.results_format }}"
uvx zizmor --format=${{ inputs.results_format }} --config=zizmor.yml . > "$results_file" || true

Check failure on line 28 in .github/actions/zizmor-action/action.yml

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

run-shell-injection

Using variable interpolation ... with github context data in a run step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead use an intermediate environment variable with env to store the data and use the environment variable in the run script. Be sure to use doublequotes the environment variable like this ENVVAR.
env:
GH_TOKEN: ${{ inputs.gh_token }}
shell: bash
- name: Set results file path output
id: set_results_file_path_output
run: echo "results_file_path=/tmp/zizmor-results.${{ inputs.results_format }}" >> $GITHUB_OUTPUT

Check failure on line 34 in .github/actions/zizmor-action/action.yml

View workflow job for this annotation

GitHub Actions / semgrep-oss/scan

run-shell-injection

Using variable interpolation ... with github context data in a run step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead use an intermediate environment variable with env to store the data and use the environment variable in the run script. Be sure to use doublequotes the environment variable like this ENVVAR.
shell: bash
104 changes: 104 additions & 0 deletions .github/workflows/security-default-branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
on:
workflow_call:
inputs: { }
secrets:
DEPENDENCY_TRACK_AUTOMATION_API_KEY:
required: true
DEFECTDOJO_API_TOKEN:
required: true
# GH_TOKEN:
# required: true
push:
branches:
- main

name: Security

permissions:
contents: read

jobs:
sbom-trivy:
runs-on: ubuntu-latest
container:
image: aquasec/trivy:0.67.2
steps:
- run: trivy --version
- uses: actions/checkout@v3
with:
fetch-depth: 0
Comment on lines +27 to +29

Check warning

Code scanning / zizmor

credential persistence through GitHub Actions artifacts

credential persistence through GitHub Actions artifacts
persist-credentials: false
- run: trivy fs --format cyclonedx --output /tmp/trivy-cyclonedx.json .
- run: apk --no-cache add curl
- run: |
curl -X "POST" "https://dependency-track-sbom.corp.zoo.dev/api/v1/bom" \
-H 'Content-Type: multipart/form-data' \
-H "X-Api-Key: ${{ secrets.DEPENDENCY_TRACK_AUTOMATION_API_KEY }}" \
-F "autoCreate=true" \
-F "projectName=$PROJECT_NAME" \
-F "projectVersion=$PROJECT_VERSION" \
-F "isLatest=true" \
-F "bom=@/tmp/trivy-cyclonedx.json"
env:
PROJECT_NAME: ${{ github.repository }}
PROJECT_VERSION: ${{ github.ref_name }}

semgrep:
runs-on: ubuntu-latest
container:
image: semgrep/semgrep
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- uses: KittyCAD/gha-workflows/.github/actions/semgrep-action@security
id: semgrep
with:
show_results_in_pr: false
results_format: json
- uses: KittyCAD/gha-workflows/.github/actions/upload-defectdojo@security
with:
dd_token: ${{ secrets.DEFECTDOJO_API_TOKEN }}
report_path: ${{ steps.semgrep.outputs.results_file_path }}
scan_type: Semgrep JSON Report
engagement: Semgrep

zizmor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- uses: KittyCAD/gha-workflows/.github/actions/zizmor-action@security
id: zizmor
with:
results_format: sarif
gh_token: ${{ secrets.GITHUB_TOKEN }}
- uses: KittyCAD/gha-workflows/.github/actions/upload-defectdojo@security
with:
dd_token: ${{ secrets.DEFECTDOJO_API_TOKEN }}
report_path: ${{ steps.zizmor.outputs.results_file_path }}
scan_type: SARIF
engagement: Zizmor

scorecard:
#if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
#with:
#fetch-depth: 0
#persist-credentials: false
- uses: KittyCAD/gha-workflows/.github/actions/scorecard-action@security
id: scorecard
with:
gh_token: ${{ secrets.GITHUB_TOKEN }}
- run: git checkout main && git pull origin main
- uses: KittyCAD/gha-workflows/.github/actions/upload-defectdojo@security
with:
dd_token: ${{ secrets.DEFECTDOJO_API_TOKEN }}
report_path: ${{ steps.scorecard.outputs.results_file_path }}
scan_type: SARIF
engagement: Scorecard
25 changes: 25 additions & 0 deletions .github/workflows/security-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
on:
pull_request:

name: Security (PR)

permissions:
contents: read

jobs:
semgrep:
## Skip any PR created by dependabot to avoid permission issues:
#if: (github.actor != 'dependabot[bot]')
name: semgrep-oss/scan
runs-on: ubuntu-latest
permissions:
contents: read
container:
image: semgrep/semgrep

steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
persist-credentials: false
- uses: KittyCAD/gha-workflows/.github/actions/semgrep-action@security
24 changes: 24 additions & 0 deletions .github/workflows/security-testing-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
on:
pull_request:

name: Security (PR) (Testing)

permissions:
contents: read

jobs:
zizmor:
name: zizmor
runs-on: ubuntu-latest
permissions:
contents: read # only needed for private repos
actions: read # only needed for private repos
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
- uses: KittyCAD/gha-workflows/.github/actions/zizmor-action@security
with:
gh_token: ${{ secrets.GITHUB_TOKEN }}
Loading
Loading