Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# PolyScan Action — AI agent instructions

## What this is
A native TypeScript GitHub Action (`src/main.ts` entry) that orchestrates multiple SAST/SCA engines (Semgrep, OpenGrep, Bandit, ESLint, SpotBugs+FindSecBugs, Trivy, detekt, gitleaks, gosec, hadolint), normalizes all results into one `Finding` schema, enforces a Quality Gate, and emits SARIF/CycloneDX SBOM/job-summary outputs. Runs only on Linux x64 (`assertSupportedPlatform` in [src/tools.ts](../src/tools.ts)).
A native TypeScript GitHub Action (`src/main.ts` entry) that orchestrates multiple SAST/SCA engines (Semgrep, OpenGrep, Bandit, oxlint, ESLint, SpotBugs+FindSecBugs, Trivy, detekt, gitleaks, gosec, hadolint), normalizes all results into one `Finding` schema, enforces a Quality Gate, and emits SARIF/CycloneDX SBOM/job-summary outputs. Runs only on Linux x64 (`assertSupportedPlatform` in [src/tools.ts](../src/tools.ts)).

## Source of truth
- `src/` is the editable implementation; `src/main.ts` is the action entrypoint and owns orchestration and final failure behavior.
Expand Down
2 changes: 1 addition & 1 deletion .github/skills/engine-tool-update/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: engine-tool-update
description: "Use when updating, checking, or validating scanner and helper-tool versions in tools.lock.json, including Semgrep, OpenGrep, Trivy, gosec, gitleaks, detekt, hadolint, SpotBugs, Bandit, and ESLint."
description: "Use when updating, checking, or validating scanner and helper-tool versions in tools.lock.json, including Semgrep, OpenGrep, Trivy, gosec, gitleaks, detekt, hadolint, SpotBugs, Bandit, oxlint, and ESLint."
argument-hint: "Name the tool and requested version, or ask to check available updates."
user-invocable: true
---
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/selftest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,21 @@ jobs:
}
'

- name: Assert oxlint scans TypeScript
run: |
node -e '
const fs = require("node:fs");
const sarif = JSON.parse(fs.readFileSync("polyscan.sarif", "utf8"));
const finding = sarif.runs.flatMap(run => run.results ?? []).find(result =>
result.ruleId === "oxlint/no-eval" &&
result.locations?.[0]?.physicalLocation?.artifactLocation?.uri === "test-fixtures/src/vuln.ts"
);
if (!finding) {
console.error("expected an oxlint/no-eval finding in test-fixtures/src/vuln.ts");
process.exit(1);
}
'

- name: Upload SARIF to code scanning
if: always()
continue-on-error: true # code scanning needs GHAS (not on private repos without it)
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,22 @@ tags and must not be reused or moved.

## [Unreleased]

### Added

- New `oxlint` engine for JavaScript/TypeScript (including JSX/TSX). It runs `no-eval`,
`no-implied-eval`, `no-new-func`, `no-script-url` and `react/no-danger` from a pinned,
SHA-256-verified standalone binary, is skipped when no JS/TS files are present, and reports
files it cannot parse in the engine note instead of dropping them silently.

### Changed

- `engines: "all"` now runs `oxlint` instead of `eslint`. ESLint stays available as an opt-in
engine (`engines: "eslint,..."`) but its default parser cannot read TypeScript syntax, so it
never reported findings in `.ts`/`.tsx` files. Findings now carry `oxlint/<rule>` rule IDs, so
existing code scanning alerts from `eslint/<rule>` close and reopen under the new IDs.
- `npm run engines:check` finds the latest release of a tool in a monorepo that publishes several
products (such as `oxc-project/oxc`) by matching the tool's tag template.

### Fixed

- Bandit and Semgrep reuse a preinstalled executable only when it reports the version pinned in
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PolyScan Action

**Multi-language SAST as a native GitHub Action.** One step runs all configured security engines — Semgrep, OpenGrep, Bandit, ESLint, SpotBugs, detekt, gosec, Trivy, gitleaks, betterleaks, hadolint and zizmor — normalizes every result into a single schema, enforces a configurable **Quality Gate**, and emits **SARIF**, a **CycloneDX SBOM** and a rich **job summary** — plus optional artifact upload.
**Multi-language SAST as a native GitHub Action.** One step runs all configured security engines — Semgrep, OpenGrep, Bandit, oxlint, ESLint, SpotBugs, detekt, gosec, Trivy, gitleaks, betterleaks, hadolint and zizmor — normalizes every result into a single schema, enforces a configurable **Quality Gate**, and emits **SARIF**, a **CycloneDX SBOM** and a rich **job summary** — plus optional artifact upload.

Written in TypeScript, bundled with `@vercel/ncc`, runs as a native GitHub Action on the `node24` runtime.

Expand All @@ -27,7 +27,7 @@ jobs:
- uses: sraisl/polyscan-action@v16
with:
target: "."
engines: "semgrep,bandit,eslint,spotbugs"
engines: "semgrep,bandit,oxlint,spotbugs"
max-critical: "0"
max-high: "0"
max-medium: "50"
Expand All @@ -49,7 +49,7 @@ jobs:
| Input | Default | Description |
|---|---|---|
| `target` | `.` | Workspace-contained directory to scan |
| `engines` | `all` | `all` or comma-separated engines: `semgrep,opengrep,bandit,eslint,spotbugs,trivy,detekt,gitleaks,betterleaks,gosec,hadolint,zizmor,trufflehog`. OpenGrep and trufflehog are opt-in and are not included by `all`. |
| `engines` | `all` | `all` or comma-separated engines: `semgrep,opengrep,bandit,eslint,oxlint,spotbugs,trivy,detekt,gitleaks,betterleaks,gosec,hadolint,zizmor,trufflehog`. OpenGrep, ESLint and trufflehog are opt-in and are not included by `all`. |
| `opengrep-config` | `auto` | OpenGrep rules config: `auto`, a local path, URL, or registry ID |
| `semgrep-exclude-rules` | _(empty)_ | Comma-separated Semgrep rule IDs to exclude; other rules still scan the same files |
| `opengrep-exclude-rules` | _(empty)_ | Comma-separated OpenGrep rule IDs to exclude; other rules still scan the same files |
Expand Down Expand Up @@ -85,7 +85,8 @@ jobs:
| **Semgrep** | many | `--config auto` |
| **OpenGrep** | many | opt-in; standalone, pinned Linux x64 binary; configurable via `opengrep-config` |
| **Bandit** | Python | installed via pip on demand |
| **ESLint** | JS/TS | `no-eval` / `no-implied-eval` / `no-new-func` |
| **oxlint** | JS/TS (incl. JSX/TSX) | `no-eval` / `no-implied-eval` / `no-new-func` / `no-script-url` / `react/no-danger`; standalone Linux x64 binary; parses TypeScript natively and reports files it cannot parse in the engine note |
| **ESLint** | JS | opt-in; superseded by oxlint. `no-eval` / `no-implied-eval` / `no-new-func`; its default parser cannot read TypeScript syntax, so `.ts`/`.tsx` files are not scanned |
| **SpotBugs + FindSecBugs** | Java + Kotlin | **build-aware**: runs `mvn compile` / `gradle classes` when a build file is present (full dependency classpath), else falls back to direct `javac`/`kotlinc` |
| **Trivy** | deps + IaC | SCA (vulnerable dependencies / CVEs) + misconfig; binary downloaded on demand |
| **detekt** | Kotlin | Kotlin-native static analysis (incl. security rules) via detekt CLI; SARIF parsed |
Expand All @@ -98,7 +99,7 @@ jobs:

Python engines are installed into isolated, version-pinned environments. OpenGrep does not require Python: PolyScan uses the exact pinned executable from `PATH` when available, otherwise it downloads and caches a SHA-256-verified standalone binary. Other downloaded tools are also cached and verified with SHA-256. SpotBugs is **build-aware** — for real Java/Kotlin projects it invokes the project's own build (Maven/Gradle) so the full dependency classpath is available, which is required to detect data-flow bugs (SQLi, command injection) on **Java** (FindSecBugs does not target Kotlin bytecode). For **Kotlin** code-security use **detekt**, which analyzes Kotlin source natively. gosec is downloaded only when Go files are present and requires the Go toolchain available on the runner. Trivy runs `--offline-scan` to avoid Maven Central rate limits.

**Default: `engines: "all"` expands to** `semgrep,bandit,eslint,spotbugs,trivy,detekt,gitleaks,betterleaks,gosec,hadolint,zizmor`. Each language-specific engine (gosec, detekt, hadolint, zizmor, …) runs a quick file-presence check and is skipped with no findings and no download when its file type isn't present, so `all` stays cheap on repositories that don't use that language. OpenGrep and trufflehog are explicit opt-ins and can be selected with `engines: "opengrep,trufflehog"` or combined with other engines.
**Default: `engines: "all"` expands to** `semgrep,bandit,oxlint,spotbugs,trivy,detekt,gitleaks,betterleaks,gosec,hadolint,zizmor`. Each language-specific engine (oxlint, gosec, detekt, hadolint, zizmor, …) runs a quick file-presence check and is skipped with no findings and no download when its file type isn't present, so `all` stays cheap on repositories that don't use that language. OpenGrep, ESLint and trufflehog are explicit opt-ins and can be selected with `engines: "opengrep,trufflehog"` or combined with other engines.

`opengrep-config: "auto"` loads OpenGrep's automatic rules configuration and can require network access. Use a repository-local rule file, for example `opengrep-config: ".opengrep/rules.yml"`, for deterministic and offline-friendly scans.

Expand Down Expand Up @@ -174,15 +175,15 @@ MIT, Apache-2.0, ISC, BSD-3-Clause, BlueOak-1.0.0, 0BSD, and CC0; that file, not
the authoritative list). The scan engines below are **not** bundled or vendored: PolyScan installs
each one from its official distribution channel at scan time and invokes it as a separate
subprocess — PolyScan never links against or redistributes their code. Binary/archive downloads
(betterleaks, detekt, gitleaks, gosec, hadolint, the Kotlin compiler, opengrep, SpotBugs/FindSecBugs, Trivy,
(betterleaks, detekt, gitleaks, gosec, hadolint, the Kotlin compiler, opengrep, oxlint, SpotBugs/FindSecBugs, Trivy,
trufflehog, zizmor) are SHA-256-verified against `tools.lock.json`; Semgrep and Bandit are
installed via `pip install <tool>==<version>` and ESLint via `npm install eslint@<version>`,
pinned to an exact version but relying on PyPI/npm registry integrity rather than PolyScan's own
checksum verification.

| Engine | License |
|---|---|
| ESLint, gitleaks, betterleaks, zizmor | MIT |
| oxlint, ESLint, gitleaks, betterleaks, zizmor | MIT |
| Bandit, gosec, detekt, Kotlin compiler, Trivy | Apache-2.0 |
| Semgrep, OpenGrep, SpotBugs | LGPL-2.1 |
| FindSecBugs | LGPL-3.0 |
Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ inputs:
required: false
default: "."
engines:
description: "Comma-separated engines to run, or 'all': semgrep,opengrep,bandit,eslint,spotbugs,trivy,detekt,gitleaks,betterleaks,gosec,hadolint,zizmor,trufflehog (all excludes opt-in opengrep and trufflehog)"
description: "Comma-separated engines to run, or 'all': semgrep,opengrep,bandit,eslint,oxlint,spotbugs,trivy,detekt,gitleaks,betterleaks,gosec,hadolint,zizmor,trufflehog (all excludes opt-in opengrep, eslint and trufflehog)"
required: false
default: "all"
opengrep-config:
Expand Down
Loading
Loading