@exploitq/cli
v1.0.5
Published
ExploitQ CLI — SAST, SCA, API security, and secrets scanning for CI/CD pipelines
Maintainers
Readme
ExploitQ CLI
Developer-first application security scanner — SAST, SCA, and API security scanning with exploitability-based prioritization. Block builds on findings that are actively being exploited in the wild, not just theoretical vulnerabilities.
Table of Contents
- Installation
- Quick Start
- GitHub Actions
- Jenkins Shared Library
- Bitbucket Pipelines
- Azure Boards Export (Headless CI Mode)
- Configuration File
- CLI Reference
- Environment Variables
- Policy Modes
- Outputs
Installation
First-time setup status: the public npm and GHCR releases are produced by the
Release ExploitQ CLIworkflow (seedocs/RELEASE.md) the first time acli/v*tag is pushed. Until that tag has been pushed, use the Install from source path below.
Global npm install (Node.js 20+):
npm install -g @exploitq/cli
exploitq --versionDocker:
docker pull ghcr.io/resilienceforge-exploitq/exploitq-cli:latest
docker run --rm -v "$(pwd):/workspace" \
-e EXPLOITQ_API_URL=https://app.exploitqsecurity.com \
-e EXPLOITQ_TOKEN=ek_... \
ghcr.io/resilienceforge-exploitq/exploitq-cli:latest \
scan --path /workspace --uploadInstall from source
Use this path when (a) the public packages haven't been published yet, (b) you're on an air-gapped network that can't reach npmjs.com or ghcr.io, or (c) you want to run an unreleased build.
# Clone & build (requires Node.js 20+ and pnpm 10+)
git clone https://github.com/ResilienceForge-ExploitQ/ExploitQ.git
cd ExploitQ
pnpm install --frozen-lockfile
pnpm --filter @exploitq/cli build
# Run directly
node artifacts/cli/dist/index.js --version
# Or install globally from the local checkout
cd artifacts/cli && npm install -g .
exploitq --version
# Or build the Docker image locally
cd artifacts/cli && docker build -t exploitq-cli:local .
docker run --rm -v "$(pwd):/workspace" exploitq-cli:local --versionQuick Start
# Scan current directory (SAST + SCA + API security)
exploitq scan
# Scan with policy enforcement — fail the build on critical/high findings
exploitq scan --policy-mode hard_fail --fail-on CRITICAL,HIGH
# Upload results to the ExploitQ dashboard
EXPLOITQ_API_URL=https://app.exploitqsecurity.com \
EXPLOITQ_TOKEN=ek_your_token_here \
exploitq scan --upload
# Output SARIF for IDE integration or GitHub Security tab
exploitq scan --format sarif --output ./resultsGitHub Actions
Add ExploitQ to any workflow. Results appear inline on PR diffs and in the GitHub Security → Code Scanning tab.
Minimal setup
name: Security Scan
on:
pull_request:
push:
branches: [main]
jobs:
exploitq:
name: ExploitQ Security Scan
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # required for SARIF upload
steps:
- uses: actions/checkout@v4
- uses: ResilienceForge-ExploitQ/ExploitQ@v1
with:
api-url: ${{ secrets.EXPLOITQ_API_URL }}
api-token: ${{ secrets.EXPLOITQ_TOKEN }}Full configuration
- uses: ResilienceForge-ExploitQ/ExploitQ@v1
with:
# ExploitQ platform connection
api-url: ${{ secrets.EXPLOITQ_API_URL }}
api-token: ${{ secrets.EXPLOITQ_TOKEN }}
# Scan configuration
path: .
scan-types: sast,sca,api # comma-separated
# Policy enforcement
policy-mode: hard_fail # advisory | soft_fail | hard_fail
fail-on: CRITICAL,HIGH # comma-separated severities
# Reporting
upload-results: true # upload to dashboard
upload-sarif: true # upload to GitHub Security tab
output-dir: exploitq-results # where to write JSON + SARIF
# Miscellaneous
cli-version: latest # pin to e.g. 1.2.3
verbose: falseUsing outputs
- uses: ResilienceForge-ExploitQ/ExploitQ@v1
id: scan
with:
api-url: ${{ secrets.EXPLOITQ_API_URL }}
api-token: ${{ secrets.EXPLOITQ_TOKEN }}
policy-mode: advisory
- name: Print scan summary
run: |
echo "Policy result : ${{ steps.scan.outputs.policy-result }}"
echo "Total findings : ${{ steps.scan.outputs.finding-count }}"
echo "Critical : ${{ steps.scan.outputs.critical-count }}"
echo "High : ${{ steps.scan.outputs.high-count }}"
echo "Dashboard : ${{ steps.scan.outputs.dashboard-url }}"| Output | Description |
|---|---|
| policy-result | pass, fail, or advisory |
| finding-count | Total findings across all engines |
| critical-count | Critical-severity findings |
| high-count | High-severity findings |
| sarif-file | Path to the generated SARIF file |
| dashboard-url | Link to the scan report in the ExploitQ dashboard |
Jenkins Shared Library
1. Register the shared library
In Manage Jenkins → Configure System → Global Pipeline Libraries, add:
| Field | Value |
|---|---|
| Name | exploitq |
| Default version | main |
| Retrieval method | Modern SCM → Git |
| Project repository | https://github.com/ResilienceForge-ExploitQ/ExploitQ.git |
| Library path | artifacts/cli/jenkins |
2. Add to any Jenkinsfile
@Library('exploitq') _
pipeline {
agent any
environment {
EXPLOITQ_API_URL = credentials('EXPLOITQ_API_URL')
EXPLOITQ_TOKEN = credentials('EXPLOITQ_TOKEN')
}
stages {
stage('Checkout') {
steps { checkout scm }
}
stage('Security Scan') {
steps {
exploitqScan(
apiUrl: env.EXPLOITQ_API_URL,
token: env.EXPLOITQ_TOKEN,
path: '.',
scanTypes: 'sast,sca,api',
policyMode: 'hard_fail',
failOn: 'CRITICAL,HIGH',
upload: true,
uploadSarif: true,
)
}
}
stage('Build') {
steps { sh 'npm run build' }
}
}
post {
always {
cleanWs(patterns: [[pattern: 'exploitq-results/**', type: 'INCLUDE']])
}
}
}Jenkins shared library parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| apiUrl | String | '' | ExploitQ platform URL |
| token | String | '' | API token (use Jenkins Credentials) |
| path | String | '.' | Repository path to scan |
| scanTypes | String | 'sast,sca,api' | Comma-separated scan engines |
| policyMode | String | 'advisory' | advisory | soft_fail | hard_fail |
| failOn | String | 'CRITICAL,HIGH' | Severity thresholds |
| upload | Boolean | true | Upload results to dashboard |
| uploadSarif | Boolean | true | Archive SARIF as build artifact |
| outputDir | String | 'exploitq-results' | Result output directory |
| cliVersion | String | 'latest' | npm package version to install |
| verbose | Boolean | false | Verbose CLI output |
| nodeVersion | String | '22' | Node.js version on the agent |
Tip: If the Warnings Next Generation plugin is installed, SARIF results are automatically published as inline diff annotations in the build. Without the plugin, SARIF is archived as a build artifact.
Bitbucket Pipelines
Add the pipe to bitbucket-pipelines.yml
image: node:22
pipelines:
pull-requests:
'**':
- step:
name: ExploitQ Security Scan
script:
- pipe: ghcr.io/resilienceforge-exploitq/exploitq-pipe:latest
variables:
EXPLOITQ_API_URL: $EXPLOITQ_API_URL
EXPLOITQ_TOKEN: $EXPLOITQ_TOKEN
SCAN_TYPES: sast,sca,api
POLICY_MODE: hard_fail
FAIL_ON: CRITICAL,HIGH
UPLOAD_RESULTS: "true"
branches:
main:
- step:
name: ExploitQ Security Scan (advisory)
script:
- pipe: ghcr.io/resilienceforge-exploitq/exploitq-pipe:latest
variables:
EXPLOITQ_API_URL: $EXPLOITQ_API_URL
EXPLOITQ_TOKEN: $EXPLOITQ_TOKEN
POLICY_MODE: advisoryStore EXPLOITQ_API_URL and EXPLOITQ_TOKEN as masked Bitbucket Repository Variables (Settings → Repository variables).
Bitbucket pipe variables
| Variable | Required | Default | Description |
|---|---|---|---|
| EXPLOITQ_API_URL | No | — | ExploitQ platform URL |
| EXPLOITQ_TOKEN | No | — | API token (mask this variable) |
| SCAN_PATH | No | . | Path to scan |
| SCAN_TYPES | No | sast,sca,api | Comma-separated engines |
| POLICY_MODE | No | advisory | advisory | soft_fail | hard_fail |
| FAIL_ON | No | CRITICAL,HIGH | Severity thresholds |
| UPLOAD_RESULTS | No | true | Upload to dashboard |
| OUTPUT_DIR | No | exploitq-results | Output directory |
| VERBOSE | No | false | Verbose output |
| DEBUG | No | false | Enable set -x in the pipe script |
Azure Boards Export (Headless CI Mode)
For teams running Azure DevOps Server on-prem (or any environment where the ExploitQ SaaS dashboard cannot reach into your VCS), the CLI ships an export-boards subcommand that creates Azure Boards work items from a previous scan — entirely from your build agent, using your existing PAT. No inbound network access from ExploitQ is required.
Quickstart
# azure-pipelines.yml — runs after `exploitq scan ... --upload`
- script: exploitq export-boards --severity high --type Bug
displayName: Export findings to Azure Boards
condition: always()
env:
AZURE_DEVOPS_EXT_PAT: $(AZURE_BOARDS_PAT) # Work Items: Read & write
SYSTEM_COLLECTIONURI: $(System.CollectionUri)
SYSTEM_TEAMPROJECT: $(System.TeamProject)What it does:
- Reads
./exploitq-results/results.jsonproduced byexploitq scan. - Filters findings to those at or above the
--severitythreshold (defaulthigh). - Computes a stable per-finding fingerprint (
severity + ruleId + filePath + lineStart) and queriesaz boards queryfor an existing work item taggedexploitq-<fingerprint>. If one exists, the finding is skipped — re-runs of the same scan create zero duplicates. - For new findings, calls
az boards work-item createwith title, description, and tagsexploitq, security, <severity>, exploitq-<fingerprint>. - Prints
Created N new work items, skipped M duplicatesand exits 0 — even when 0 new items are created. Only fails on Azure CLI auth errors oraznon-zero exits.
Options
--file <path> Path to results.json (default: ./exploitq-results/results.json)
--severity <level> critical | high | medium | low | info (default: high)
--type <type> Azure Boards work-item type (default: Bug)
--organization <url> ADO org URL (or set SYSTEM_COLLECTIONURI)
--project <name> ADO project (or set SYSTEM_TEAMPROJECT)
--assignee <user> Default assignee (UPN / email)
--area-path <path> Sets System.AreaPath
--iteration-path <path> Sets System.IterationPath
--max-items <n> Safety cap per run (default: 50)
--dry-run Print what would be created without calling az
--verbose Verbose outputLocal dry-run
Verify the work items look right before turning the live step on:
exploitq scan --path . --output ./tmp
exploitq export-boards \
--file ./tmp/results.json \
--severity high \
--organization https://tfs.contoso.com/MyCollection \
--project MyProject \
--dry-run --verboseRequirements
- Azure CLI (
az) on the build agent. azure-devopsCLI extension:az extension add --name azure-devops.AZURE_DEVOPS_EXT_PATenv var with Work Items: Read & write scope.
Microsoft-hosted
ubuntu-latestagents already haveazand theazure-devopsextension pre-installed. Self-hosted Server build agents typically needaz extension add --name azure-devopsonce.
Configuration File
Create .exploitq.yml in your repository root to set project-level defaults. CLI flags and CI/CD variables override config file values.
version: 1
scan:
types: [sast, sca, api]
paths:
include:
- src/
- lib/
exclude:
- "**/__tests__/**"
- "**/node_modules/**"
- "**/vendor/**"
api:
spec_paths:
- openapi.yml
- docs/api.yaml
policy:
mode: hard_fail # advisory | soft_fail | hard_fail
fail_on: [CRITICAL, HIGH]
block_on_actively_exploited: true # block even MEDIUM if KEV/CISA listed
epss_threshold: 0.5 # block if EPSS score > 50 %
reporting:
formats: [json, sarif]
upload: true
metadata:
service: payments-api
team: platform-securityCLI Reference
exploitq scan
Run SAST, SCA, and API security scans.
Options:
--path <dir> Path to scan (default: ".")
--scan-types <types> Comma-separated engines: sast,sca,api (default: "sast,sca,api")
--provider <p> CI/CD provider: github|gitlab|ado|bitbucket|jenkins|local (default: "local")
--repo <repo> Repository full name (e.g. org/repo)
--commit <sha> Git commit SHA
--branch <branch> Branch name
--pr <number> Pull / merge request number
--format <formats> Output formats: json,sarif (default: "json")
--output <dir> Output directory (default: "./exploitq-results")
--upload Upload results to ExploitQ API
--policy-mode <mode> Policy mode: advisory|soft_fail|hard_fail
--config <file> Path to .exploitq.yml config file
--verbose Verbose outputExit codes:
| Code | Meaning |
|---|---|
| 0 | Scan complete, policy passed (or advisory mode) |
| 1 | Policy gate failed (hard_fail + findings above threshold) |
| 3 | Invalid path or config file error |
| 4 | Upload error in non-advisory mode |
exploitq upload
Upload a pre-existing results.json to the ExploitQ dashboard.
Options:
--file <path> Path to results.json (default: "./exploitq-results/results.json")
--verbose Verbose outputexploitq policy-check
Re-evaluate the policy for a results.json without re-scanning.
Options:
--file <path> Path to results.json (default: "./exploitq-results/results.json")
--policy-mode <mode> Policy mode override
--fail-on <sevs> Severity threshold override
--verbose Verbose outputexploitq export-boards
Create Azure Boards work items from a previous scan. See Azure Boards Export (Headless CI Mode) above for the full quickstart and option reference.
Environment Variables
| Variable | Description |
|---|---|
| EXPLOITQ_API_URL | ExploitQ platform URL (e.g. https://app.exploitqsecurity.com) |
| EXPLOITQ_TOKEN | API token — generate one in Settings → API Keys |
Policy Modes
| Mode | Behavior |
|---|---|
| advisory | Record all findings. Never fail the build. Best for initial rollout. |
| soft_fail | Print a warning when findings exceed the threshold but exit 0. |
| hard_fail | Exit 1 when findings exceed the threshold. Blocks merges and deployments. |
Secrets setup checklist
- Generate an API token in ExploitQ → Settings → API Keys.
- Add it as a repository secret:
- GitHub Actions:
EXPLOITQ_TOKEN+EXPLOITQ_API_URL - Jenkins: Jenkins Credentials store (Secret Text) with IDs
EXPLOITQ_TOKENandEXPLOITQ_API_URL - Bitbucket: Repository variables (enable Masked)
- GitHub Actions:
- Start with
policy-mode: advisoryuntil the finding baseline is understood, then tighten tosoft_fail→hard_fail.
License
Proprietary — exploitq.com
