From 2f2956b13132d032ce28dd8c7058f0a73b750010 Mon Sep 17 00:00:00 2001 From: Error Date: Fri, 27 Feb 2026 14:17:38 -0600 Subject: [PATCH 1/6] ci: add ClamAV and OWASP security scan workflows --- .github/workflows/clam-av.yml | 82 ++++++++++++++++++++++++++++++++ .github/workflows/owasp-scan.yml | 66 +++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 .github/workflows/clam-av.yml create mode 100644 .github/workflows/owasp-scan.yml diff --git a/.github/workflows/clam-av.yml b/.github/workflows/clam-av.yml new file mode 100644 index 00000000000..345a23238bf --- /dev/null +++ b/.github/workflows/clam-av.yml @@ -0,0 +1,82 @@ +name: av-clamav +on: + pull_request: + release: + types: [published] + workflow_dispatch: + +permissions: + contents: read + actions: read + +jobs: + clamav: + runs-on: ubuntu-latest + steps: + # Checkout the right ref + - name: Checkout (release tag) + if: github.event_name == 'release' + uses: actions/checkout@v4 + with: + ref: ${{ github.event.release.tag_name }} + - name: Checkout (PR/default) + if: github.event_name != 'release' + uses: actions/checkout@v4 + + # Single source-of-truth build -> one file + - name: Build and package + id: build + uses: ./.github/actions/build-package + + # Install fresh ClamAV DB + - name: Install & update ClamAV DB + run: | + set -e + sudo apt-get update + sudo apt-get install -y clamav clamav-freshclam unzip + sudo systemctl stop clamav-freshclam || true + sudo mkdir -p /var/lib/clamav + sudo chown -R clamav:clamav /var/lib/clamav + sudo freshclam --verbose + ls -lh /var/lib/clamav + + # Scan extracted bundle so counts reflect actual files + - name: Verify ClamAV detects EICAR signature + run: | + set -euo pipefail + printf 'X5O!P%%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > eicar.com + status=0 + clamscan eicar.com > eicar.log || status=$? + cat eicar.log + if [ "$status" -ne 1 ]; then + echo "ClamAV failed to report the EICAR signature" >&2 + exit 1 + fi + grep -q 'eicar.com: Eicar-Test-Signature FOUND' eicar.log + grep -q 'Infected files: 1' eicar.log + rm -f eicar.com eicar.log + + - name: Extract bundle and scan + run: | + set -euo pipefail + rm -rf scan && mkdir -p scan + unzip -q bundle/opencode.zip -d scan + echo "File count in payload: $(find scan -type f | wc -l)" + clamscan -ri --scan-archive=yes scan | tee clamav.log + if grep -qE 'Infected files: [1-9][0-9]*' clamav.log; then + findings=$(grep 'FOUND' clamav.log | grep -v 'Eicar-Test-Signature' || true) + if [ -n "${findings}" ]; then + echo "Unexpected detections found:" >&2 + echo "${findings}" >&2 + exit 1 + fi + echo 'Only EICAR detections observed; continuing.' + fi + + - name: Upload scan results + uses: actions/upload-artifact@v4 + with: + name: clamav-scan-results + path: | + clamav.log + bundle/opencode.zip diff --git a/.github/workflows/owasp-scan.yml b/.github/workflows/owasp-scan.yml new file mode 100644 index 00000000000..46ffcf4af45 --- /dev/null +++ b/.github/workflows/owasp-scan.yml @@ -0,0 +1,66 @@ +name: owasp-dependency-check +on: + pull_request: + release: + types: [published] + workflow_dispatch: + +permissions: + contents: read + security-events: write + +jobs: + depcheck: + runs-on: ubuntu-latest + steps: + - name: Checkout (release tag) + if: github.event_name == 'release' + uses: actions/checkout@v4 + with: + ref: ${{ github.event.release.tag_name }} + - name: Checkout (PR/default) + if: github.event_name != 'release' + uses: actions/checkout@v4 + + - name: Setup Bun (repo action) + uses: ./.github/actions/setup-bun + + - name: Install workspace deps (Bun) + run: bun install --frozen-lockfile || bun install + + - name: Ensure per-package node_modules (symlink to root) + run: | + set -e + root_nm="$(pwd)/node_modules" + if [ ! -d "$root_nm" ]; then echo 'No root node_modules after bun install' >&2; exit 1; fi + # create a node_modules symlink in every workspace package that lacks one + git ls-files -z | tr '\0' '\n' | grep -E '(^|/)package.json$' | while read -r pj; do + pkgdir="$(dirname "$pj")" + [ "$pkgdir" = ".github/actions/setup-bun" ] && continue + if [ ! -d "$pkgdir/node_modules" ]; then + echo "linking $pkgdir/node_modules -> $root_nm" + ln -s "$root_nm" "$pkgdir/node_modules" || true + fi + done + + - name: Cache dependency-check data + uses: actions/cache@v4 + with: + path: ~/.m2/repository/org/owasp/dependency-check-data/ + key: depcheck-data-${{ runner.os }}-v2 + restore-keys: | + depcheck-data-${{ runner.os }}- + + - name: Run OWASP Dependency-Check + uses: dependency-check/Dependency-Check_Action@1.1.0 + with: + project: OpenCode + path: . + format: ALL + args: --enableExperimental + + - name: Upload reports + uses: actions/upload-artifact@v4 + with: + name: owasp-depcheck-report + path: reports/** From 6add62a4f294135830a4d117260f99797efe2785 Mon Sep 17 00:00:00 2001 From: Error Date: Fri, 27 Feb 2026 14:25:16 -0600 Subject: [PATCH 2/6] ci: add build-package action for ClamAV workflow --- .github/actions/build-package/action.yml | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/actions/build-package/action.yml diff --git a/.github/actions/build-package/action.yml b/.github/actions/build-package/action.yml new file mode 100644 index 00000000000..618c78c1c9a --- /dev/null +++ b/.github/actions/build-package/action.yml @@ -0,0 +1,33 @@ +name: Build and package (Bun -> single zip) +description: Build with Bun (Turbo) and package a single distributable archive +outputs: + archive_path: + description: Absolute path to the archive + value: ${{ steps.pkg.outputs.archive_path }} +runs: + using: composite + steps: + - name: Setup Bun (from .bun-version) + uses: ./.github/actions/setup-bun + + - name: Build (Turbo) + shell: bash + run: bunx turbo run build + + - name: Ensure zip is available + shell: bash + run: sudo apt-get update -y && sudo apt-get install -y zip + + - name: Package single file + id: pkg + shell: bash + run: | + set -e + mkdir -p bundle + if [ -d dist ]; then SRC=dist; elif [ -d build ]; then SRC=build; else SRC=.; fi + if [ "$SRC" = "." ]; then + zip -r bundle/opencode.zip . -x '.git/*' '.github/*' 'node_modules/*' + else + (cd "$SRC" && zip -r ../bundle/opencode.zip .) + fi + echo "archive_path=$(pwd)/bundle/opencode.zip" >> "$GITHUB_OUTPUT" From 8158d37432c3b66d372f225115a2bbcdfe83c665 Mon Sep 17 00:00:00 2001 From: Error Date: Fri, 27 Feb 2026 14:51:40 -0600 Subject: [PATCH 3/6] ci: make security scan workflows act-compatible --- .github/actions/build-package/action.yml | 5 ++++- .github/workflows/clam-av.yml | 1 + .github/workflows/owasp-scan.yml | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/actions/build-package/action.yml b/.github/actions/build-package/action.yml index 618c78c1c9a..2c89a9a493d 100644 --- a/.github/actions/build-package/action.yml +++ b/.github/actions/build-package/action.yml @@ -12,7 +12,10 @@ runs: - name: Build (Turbo) shell: bash - run: bunx turbo run build + run: | + if ! OPENCODE_CHANNEL=latest bunx turbo run build --env-mode=loose; then + echo "Turbo build failed; continuing with source packaging for security scan." + fi - name: Ensure zip is available shell: bash diff --git a/.github/workflows/clam-av.yml b/.github/workflows/clam-av.yml index 345a23238bf..6214106fe8c 100644 --- a/.github/workflows/clam-av.yml +++ b/.github/workflows/clam-av.yml @@ -74,6 +74,7 @@ jobs: fi - name: Upload scan results + if: ${{ env.ACTIONS_RUNTIME_TOKEN != '' }} uses: actions/upload-artifact@v4 with: name: clamav-scan-results diff --git a/.github/workflows/owasp-scan.yml b/.github/workflows/owasp-scan.yml index 46ffcf4af45..978d0813c98 100644 --- a/.github/workflows/owasp-scan.yml +++ b/.github/workflows/owasp-scan.yml @@ -60,6 +60,7 @@ jobs: args: --enableExperimental - name: Upload reports + if: ${{ env.ACTIONS_RUNTIME_TOKEN != '' }} uses: actions/upload-artifact@v4 with: name: owasp-depcheck-report From 61ef76f78f1cc633304851323aa7ee7d1570c470 Mon Sep 17 00:00:00 2001 From: Error Date: Fri, 27 Feb 2026 16:49:15 -0600 Subject: [PATCH 4/6] ci: run bun audit on PRs and trivy on releases --- .github/workflows/owasp-scan.yml | 79 ++++++++++++++------------------ 1 file changed, 35 insertions(+), 44 deletions(-) diff --git a/.github/workflows/owasp-scan.yml b/.github/workflows/owasp-scan.yml index 978d0813c98..8a85b58842b 100644 --- a/.github/workflows/owasp-scan.yml +++ b/.github/workflows/owasp-scan.yml @@ -1,4 +1,4 @@ -name: owasp-dependency-check +name: dependency-security on: pull_request: release: @@ -7,10 +7,25 @@ on: permissions: contents: read - security-events: write jobs: - depcheck: + bun-audit: + if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version-file: package.json + + - name: Run Bun audit (prod, high+) + run: bun audit --prod --audit-level=high + + trivy-release: + if: github.event_name == 'release' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: - name: Checkout (release tag) @@ -18,50 +33,26 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.release.tag_name }} - - name: Checkout (PR/default) + - name: Checkout (default) if: github.event_name != 'release' uses: actions/checkout@v4 - - name: Setup Bun (repo action) - uses: ./.github/actions/setup-bun - - - name: Install workspace deps (Bun) - run: bun install --frozen-lockfile || bun install - - - name: Ensure per-package node_modules (symlink to root) - run: | - set -e - root_nm="$(pwd)/node_modules" - if [ ! -d "$root_nm" ]; then echo 'No root node_modules after bun install' >&2; exit 1; fi - # create a node_modules symlink in every workspace package that lacks one - git ls-files -z | tr '\0' '\n' | grep -E '(^|/)package.json$' | while read -r pj; do - pkgdir="$(dirname "$pj")" - [ "$pkgdir" = ".github/actions/setup-bun" ] && continue - if [ ! -d "$pkgdir/node_modules" ]; then - echo "linking $pkgdir/node_modules -> $root_nm" - ln -s "$root_nm" "$pkgdir/node_modules" || true - fi - done - - - name: Cache dependency-check data - uses: actions/cache@v4 + - name: Run Trivy vulnerability scan + uses: aquasecurity/trivy-action@0.34.1 with: - path: ~/.m2/repository/org/owasp/dependency-check-data/ - key: depcheck-data-${{ runner.os }}-v2 - restore-keys: | - depcheck-data-${{ runner.os }}- - - - name: Run OWASP Dependency-Check - uses: dependency-check/Dependency-Check_Action@1.1.0 - with: - project: OpenCode - path: . - format: ALL - args: --enableExperimental - - - name: Upload reports - if: ${{ env.ACTIONS_RUNTIME_TOKEN != '' }} + scan-type: fs + scan-ref: . + scanners: vuln + vuln-type: library + severity: HIGH,CRITICAL + ignore-unfixed: true + format: sarif + output: trivy-results.sarif + exit-code: '1' + + - name: Upload Trivy report + if: ${{ always() && env.ACTIONS_RUNTIME_TOKEN != '' }} uses: actions/upload-artifact@v4 with: - name: owasp-depcheck-report - path: reports/** + name: trivy-release-report + path: trivy-results.sarif From b83dce481faffe71c0655f6b5267673b45c919e3 Mon Sep 17 00:00:00 2001 From: Error Date: Sat, 28 Feb 2026 15:20:23 -0600 Subject: [PATCH 5/6] ci: improve release trivy signal --- .github/workflows/owasp-scan.yml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/owasp-scan.yml b/.github/workflows/owasp-scan.yml index 8a85b58842b..cb7056d9db5 100644 --- a/.github/workflows/owasp-scan.yml +++ b/.github/workflows/owasp-scan.yml @@ -40,6 +40,7 @@ jobs: - name: Run Trivy vulnerability scan uses: aquasecurity/trivy-action@0.34.1 with: + version: v0.69.1 scan-type: fs scan-ref: . scanners: vuln @@ -48,11 +49,26 @@ jobs: ignore-unfixed: true format: sarif output: trivy-results.sarif - exit-code: '1' + exit-code: "1" + + - name: Run Trivy misconfig and secret scan + uses: aquasecurity/trivy-action@0.34.1 + with: + version: v0.69.1 + scan-type: fs + scan-ref: . + scanners: misconfig,secret + severity: HIGH,CRITICAL + skip-dirs: node_modules,packages/opencode/dist,tree-sitter-scheme + format: sarif + output: trivy-policy-results.sarif + exit-code: "0" - name: Upload Trivy report if: ${{ always() && env.ACTIONS_RUNTIME_TOKEN != '' }} uses: actions/upload-artifact@v4 with: name: trivy-release-report - path: trivy-results.sarif + path: | + trivy-results.sarif + trivy-policy-results.sarif From 58939d42bc5dbce89ae28717bbd0e16d36291ede Mon Sep 17 00:00:00 2001 From: Error Date: Sat, 28 Feb 2026 15:55:59 -0600 Subject: [PATCH 6/6] ci: streamline release security workflows --- .github/workflows/clam-av.yml | 1 - .github/workflows/{owasp-scan.yml => dependency-security.yml} | 0 2 files changed, 1 deletion(-) rename .github/workflows/{owasp-scan.yml => dependency-security.yml} (100%) diff --git a/.github/workflows/clam-av.yml b/.github/workflows/clam-av.yml index 6214106fe8c..a98562c68b0 100644 --- a/.github/workflows/clam-av.yml +++ b/.github/workflows/clam-av.yml @@ -1,6 +1,5 @@ name: av-clamav on: - pull_request: release: types: [published] workflow_dispatch: diff --git a/.github/workflows/owasp-scan.yml b/.github/workflows/dependency-security.yml similarity index 100% rename from .github/workflows/owasp-scan.yml rename to .github/workflows/dependency-security.yml