Repository navigation
Conversion of CI tooling from BrightCodeCompany repo #13
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
Changes from all commits
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,65 @@ | ||
| name: Generate Vulnerability Report | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| imageTag: | ||
| description: 'Image Tag to scan' | ||
| required: true | ||
| default: 'latest' | ||
| type: string | ||
|
|
||
| permissions: | ||
| contents: read | ||
| issues: write | ||
|
|
||
| jobs: | ||
| run-scan: | ||
| runs-on: ubuntu-latest | ||
| environment: dockerhub | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
|
|
||
| - uses: actions/setup-node@v6 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (non-blocking): latest version is v7 |
||
| with: | ||
| node-version: 26 | ||
|
|
||
| - name: Login to Docker Hub | ||
| uses: docker/login-action@v4 | ||
| with: | ||
| username: ${{ vars.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_TOKEN }} | ||
|
|
||
| - name: Get sha256 digest of requested tag | ||
| id: digest | ||
| env: | ||
| IMAGE_TAG: ${{ inputs.imageTag }} | ||
| run: | | ||
| digest=$(docker buildx imagetools inspect "openintegrationengine/engine:${IMAGE_TAG}" --raw \ | ||
| | jq -r '.manifests[] | select(.platform.os == "linux" and .platform.architecture == "amd64") | .digest') | ||
|
|
||
| echo "Got digest: $digest" | ||
| echo "imageDigest=$digest" >> "$GITHUB_OUTPUT" | ||
| echo "startTime=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Docker Scout | ||
| id: docker-scout | ||
| uses: docker/scout-action@v1 | ||
| with: | ||
| command: cves | ||
| image: openintegrationengine/engine@${{ steps.digest.outputs.imageDigest }} | ||
| platform: linux/amd64 | ||
| only-severities: critical,high | ||
| ignore-base: true | ||
| sarif-file: reports/sarif.json | ||
|
|
||
| - name: Render report | ||
| working-directory: ./reports | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| IMAGE_TAG: ${{ inputs.imageTag }} | ||
| IMAGE_DIGEST: ${{ steps.digest.outputs.imageDigest }} | ||
| START_TIME: ${{ steps.digest.outputs.startTime }} | ||
| run: | | ||
| node render-report.ts "$IMAGE_TAG" "$IMAGE_DIGEST" "$START_TIME" | ||
| gh issue create --title "OpenIntegrationEngine ${IMAGE_TAG} vulnerabilities" --body-file report.md | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| name: Render Docker templates | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| pull_request: | ||
| paths: | ||
| - releases.yaml | ||
| - render_docker_templates.sh | ||
| - templates/** | ||
| - dockerfiles/** | ||
| - .github/workflows/render-docker-templates.yaml | ||
|
|
||
| jobs: | ||
| render-dockerfiles: | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| GOMPLATE_VERSION: 4.3.2 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (non-blocking): Latest version is |
||
| GOMPLATE_SHA512: ed77ea781cc2b63fbb325dbb29bbe3041f5158ccaec555fdc79b6ecca1b4c48fb6be2fee6612469c715b77fb9179be6e263ee1d58773232dfc346249afe660ab | ||
| GOMPLATE_DIR: ${{ github.workspace }}/.cache/gomplate | ||
| GOMPLATE_BIN: ${{ github.workspace }}/.cache/gomplate/gomplate | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (non-blocking): Latest version is v7 |
||
|
|
||
| - name: Check for duplicate slugs | ||
| run: | | ||
| duplicates=$(yq -o=json -I=0 '.versions | map(.slug) | group_by(.) | map(select(length > 1) | .[0])' releases.yaml) | ||
| if [ "$duplicates" != "[]" ]; then | ||
| echo "::error file=releases.yaml::Duplicate version slugs: $duplicates" | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Cache gomplate binary | ||
| id: cache | ||
| uses: actions/cache@v5 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (non-blocking): Latest version is v6 |
||
| with: | ||
| path: ${{ env.GOMPLATE_DIR }} | ||
| key: | | ||
| gomplate-${{ env.GOMPLATE_VERSION }}-${{ env.GOMPLATE_SHA512 }} | ||
|
|
||
| - name: Download gomplate | ||
| if: steps.cache.outputs.cache-hit != 'true' | ||
| shell: bash | ||
| run: | | ||
| mkdir -p "${GOMPLATE_DIR}" | ||
| curl -fsSL --output "${GOMPLATE_BIN}" "https://github.com/hairyhenderson/gomplate/releases/download/v$GOMPLATE_VERSION/gomplate_linux-amd64" | ||
| echo "${{ env.GOMPLATE_SHA512 }} ${GOMPLATE_BIN}" | sha512sum --check --status | ||
| chmod +x "${GOMPLATE_BIN}" | ||
|
|
||
| - name: Add gomplate to PATH | ||
| shell: bash | ||
| run: echo "${GOMPLATE_DIR}" >> "${GITHUB_PATH}" | ||
|
|
||
| - name: Render Dockerfiles | ||
| run: ./render_docker_templates.sh | ||
|
|
||
| - name: Check rendered files are up to date | ||
| run: | | ||
| git add --intent-to-add dockerfiles | ||
| if ! git diff --exit-code -- dockerfiles; then | ||
| echo "::error::dockerfiles/ is out of date. Run ./render_docker_templates.sh and commit the result." | ||
| exit 1 | ||
| fi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| name: Update description in Docker Hub | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - README.md | ||
|
|
||
| concurrency: dockerhub-description | ||
|
|
||
| jobs: | ||
| update-description: | ||
| runs-on: ubuntu-latest | ||
| environment: dockerhub | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (non-blocking): Latest version is v7 |
||
|
|
||
| - name: Docker Hub Description | ||
| uses: peter-evans/dockerhub-description@v5 | ||
| with: | ||
| username: ${{ vars.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_TOKEN }} | ||
| repository: openintegrationengine/engine | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,3 @@ | ||
| .idea/ | ||
| .idea/ | ||
| reports/sarif.json | ||
| reports/report.md |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| #!/usr/bin/env bash | ||
| # | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # SPDX-FileCopyrightText: 2026 BrightCodeCompany OÜ | ||
| # | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| # Scan output and the report are written next to this script | ||
| cd "$(dirname "$0")" | ||
|
|
||
| imageRepo=openintegrationengine/engine | ||
| imageTag=${1:-latest} | ||
|
|
||
| digest=$(docker buildx imagetools inspect "$imageRepo:$imageTag" --raw \ | ||
| | jq -r '.manifests[] | select(.platform.os == "linux" and .platform.architecture == "amd64") | .digest') | ||
|
|
||
| echo "Got digest: $digest" | ||
| startTime=$(date -u +"%Y-%m-%dT%H:%M:%SZ") | ||
|
|
||
| docker scout cves "$imageRepo@$digest" --ignore-base --only-severity critical,high --format sarif -o sarif.json | ||
|
|
||
| # Requires at least Node 26 | ||
| node render-report.ts "$imageTag" "$digest" "$startTime" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| // SPDX-License-Identifier: MPL-2.0 | ||
| // SPDX-FileCopyrightText: 2026 BrightCodeCompany OÜ | ||
|
|
||
| import fs from 'fs' | ||
|
|
||
| const severityToColor = (severity: string) => { | ||
| switch (severity.toLowerCase()) { | ||
| case 'critical': | ||
| return '🔴' | ||
| case 'high': | ||
| return '🟠' | ||
| case 'medium': | ||
| return '🟡' | ||
| case 'low': | ||
| return '⚪' | ||
| } | ||
| } | ||
|
|
||
| const getReportJson = () => { | ||
| try { | ||
| const report = fs.readFileSync('sarif.json', 'utf8') | ||
| return JSON.parse(report) | ||
| } catch (err) { | ||
| console.error(err) | ||
| process.exit(1) | ||
| } | ||
| } | ||
|
|
||
| const writeReportToFile = (markdown: string) => { | ||
| try { | ||
| fs.writeFileSync('report.md', markdown) | ||
| } catch (err) { | ||
| console.error(err) | ||
| process.exit(1) | ||
| } | ||
| } | ||
|
|
||
| const renderCve = (cve: object) => { | ||
| const pUrl = cve.properties.purls[0] | ||
| const packageName = pUrl.replace("pkg:", "").split("@")[0] | ||
| const installedVersion = pUrl.split("@")[1] | ||
| const severity = cve.properties.cvssV3_severity | ||
| const sevScore = cve.properties.cvssV3 | ||
| const fixedVersion = cve.properties.fixed_version | ||
| return `| [${cve.id}](${cve.helpUri}) | ${severityToColor(severity)} | ${sevScore} | \`${packageName}\` | \`${installedVersion}\` | \`${fixedVersion}\` |` | ||
| } | ||
|
|
||
| const main = (imageTag: string, imageDigest: string, startTime: Instant) => { | ||
| const report = getReportJson().runs[0] | ||
|
|
||
| const sortedCve = report.tool.driver.rules.sort((a: object, b: object) => { | ||
| return b.properties.cvssV3 - a.properties.cvssV3 | ||
| }) | ||
|
|
||
| let cveStrings: string[] = [] | ||
| let cvssMap: { [severity: string]: number } = {} | ||
| for (const cve of sortedCve) { | ||
| const cveString = renderCve(cve) | ||
|
|
||
| cvssMap[cve.properties.cvssV3_severity] = (cvssMap[cve.properties.cvssV3_severity] || 0) + 1 | ||
| cveStrings.push(cveString) | ||
| } | ||
| const driver = report.tool.driver | ||
|
|
||
| const markdown = ` | ||
| ## Scan | ||
|
|
||
| | | | | ||
| |-------------|-----------------------------------------------------------------| | ||
| | **Target** | \`${imageTag}\` | | ||
| | **Digest** | \`${imageDigest}\` | | ||
| | **Scanner** | \`${driver.fullName} ${driver.version}\` | | ||
| | **Timestamp** | \`${startTime.toString()}\` | | ||
| | **Filter** | \`severity ≥ High; base-image vulns excluded\` | | ||
|
|
||
| **Totals:** 🔴 Critical \`${cvssMap['CRITICAL'] || 0}\` · 🟠 High \`${cvssMap['HIGH'] || 0}\` · 🟡 Medium \`${cvssMap['MEDIUM'] || 0}\` · ⚪ Low \`${cvssMap['LOW'] || 0}\` | ||
|
|
||
| ## Findings | ||
|
|
||
| | CVE | Sev | CVSS | Package | Installed | Fixed in | | ||
| |------|-----|------|---------|-----------|----------| | ||
| ${cveStrings.join('\n')} | ||
| ` | ||
| writeReportToFile(markdown) | ||
| } | ||
|
|
||
| const args = process.argv.slice(2) | ||
| console.log(args) | ||
|
|
||
| const imageTag = `openintegrationengine/engine:${args[0]}` | ||
| const imageDigest = args[1] | ||
| const startInstant = Temporal.Instant.from(args[2]) | ||
|
|
||
| main(imageTag, imageDigest, startInstant) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (non-blocking): latest version is v7