-
Notifications
You must be signed in to change notification settings - Fork 0
Add security workflows #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
be058bf
82babdf
6fafa1d
50e8b2c
dad171a
d10bbbc
57e22ec
ffa90e7
cdbf347
b2c8fc4
fce8814
e39c8b1
46d2ee8
d87984c
27c480a
4e63298
3303073
b570330
abaad2e
1f6db5c
09854ee
d7d537a
fced3e2
df8071f
ba955c6
f12d90d
ad00d0c
f87de96
0406ba4
2acb056
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
| 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
|
||
| 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
|
||
| 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 | ||
| 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 |
| 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
|
||
| 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
|
||
| shell: bash | ||
| 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 | ||
| 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 | ||
| 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 |
| 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 }} |
Check warning
Code scanning / zizmor
credential persistence through GitHub Actions artifacts