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
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