This Checkmarx tutorial walks through the practical path most teams actually take: understand what the platform scans, run a first static analysis scan against a real repository, triage the results honestly, and then automate the scan in CI so it runs on every pull request. Checkmarx is one of the established application security testing platforms, and its power comes with a learning curve around configuration and noise management. The goal here is to get a useful scan running and, just as important, to keep it from drowning your team in findings.
What Checkmarx actually scans
Checkmarx is best known for static application security testing (SAST), where it analyzes source code without running it to find vulnerabilities like injection flaws, cross-site scripting, and insecure data handling. The modern platform, Checkmarx One, bundles several scan types under one roof:
- SAST — source-code analysis for vulnerability patterns.
- SCA — software composition analysis for vulnerable open-source dependencies.
- IaC security — misconfiguration scanning for Terraform, Kubernetes, and similar.
- Secrets and container scanning — depending on your license tier.
This tutorial focuses on SAST, since that is the capability most teams adopt first and the one Checkmarx is historically strongest at.
How SAST works, so the results make sense
Before running a scan, it helps to know what the engine does. SAST builds a model of your code and traces data flow from sources (where untrusted input enters, like an HTTP parameter) to sinks (where it does something dangerous, like a SQL query or a shell command). When tainted data reaches a sink without passing through a sanitizer the engine recognizes, it reports a finding.
That model explains both the strength and the frustration of SAST. It catches real injection paths across files and functions, but it also produces false positives when it cannot see that a custom function sanitizes input. Knowing this up front changes how you read results: a finding is a hypothesis to verify, not a confirmed bug.
Running your first scan with the CLI
The fastest way to learn Checkmarx is the CLI. After your organization provisions access and you have your credentials, authenticate and kick off a scan against a project directory:
# Authenticate (values come from your Checkmarx One tenant)
export CX_BASE_URI="https://your-tenant.checkmarx.net"
export CX_TENANT="your-tenant"
export CX_APIKEY="****"
# Run a scan of the current repository
cx scan create \
--project-name "my-app" \
--source ./ \
--scan-types sast \
--branch main
The scan uploads your source, analyzes it server-side, and returns a scan ID. Retrieve the results as JSON so you can inspect or post-process them:
cx results show --scan-id <SCAN_ID> --report-format json --output-name results
For a first run, scan a single service rather than a giant monorepo. A smaller scope finishes faster and gives you a manageable set of findings to learn triage on.
Triaging findings without burning out
The first scan of a mature codebase often returns hundreds of findings. Working through them top to bottom is how teams give up on a tool. Triage by priority instead:
- Filter to high and critical severity first. These are where genuine, exploitable bugs concentrate.
- Confirm reachability. Ask whether the source is actually attacker-controllable and the sink actually runs. Many findings collapse under this question.
- Mark false positives with a reason. Checkmarx lets you set a predicate state (for example, "not exploitable") with a note. Do this deliberately, because the note is what a reviewer or auditor reads later.
- Fix or ticket the real ones. A confirmed finding either gets fixed in the PR or becomes a tracked issue with an owner.
Resist the urge to suppress findings just to clear the queue. A muted-but-unexamined finding is a future incident. Our Academy covers how to build a triage workflow that scales past the first scan.
Integrating Checkmarx into CI
A scan you run manually is a scan you will eventually forget. The value comes from running on every pull request and gating merges on new issues. A GitHub Actions step using the CLI looks like this:
- name: Checkmarx SAST scan
run: |
cx scan create \
--project-name "${{ github.repository }}" \
--source ./ \
--scan-types sast \
--branch "${{ github.head_ref }}" \
--report-format sarif \
--output-name cx-results
env:
CX_BASE_URI: ${{ secrets.CX_BASE_URI }}
CX_TENANT: ${{ secrets.CX_TENANT }}
CX_APIKEY: ${{ secrets.CX_APIKEY }}
Two configuration choices make or break the integration. First, gate on new findings introduced by the pull request rather than the entire historical backlog, or every PR fails on pre-existing debt and developers learn to ignore the check. Second, emit SARIF so results surface directly in the pull request's code-scanning view instead of a separate portal.
Where Checkmarx fits alongside other tools
SAST finds bugs in code you wrote. It does not cover vulnerable open-source dependencies, which is the job of software composition analysis. Most teams run SAST and SCA together because they cover different halves of the attack surface, and running a fast dependency scan alongside Checkmarx SAST closes the gap between "our code is clean" and "our supply chain is clean." If you are also evaluating dynamic testing, our DAST product page explains how runtime scanning complements static analysis.
FAQ
What is Checkmarx used for?
Checkmarx is an application security testing platform whose core capability is static application security testing (SAST), analyzing source code for vulnerabilities like injection and XSS. The broader Checkmarx One platform also offers SCA, infrastructure-as-code scanning, and other scan types.
How do I run my first Checkmarx scan?
Authenticate the Checkmarx CLI with your tenant credentials, then run cx scan create against a project directory with --scan-types sast. Start with a single service rather than a full monorepo so the scan finishes quickly and returns a manageable number of findings.
Why does Checkmarx report so many findings?
SAST traces data from input sources to dangerous sinks and flags any path it cannot prove is safe, which produces false positives when it cannot recognize a custom sanitizer. Triage by severity and reachability, mark false positives with a reason, and fix or ticket the confirmed ones.
How do I add Checkmarx to a CI pipeline?
Use the CLI in a pipeline step, output SARIF so results appear in the pull request, and configure the gate to fail only on new findings introduced by the change rather than the entire backlog. Store credentials as CI secrets, never in the repository.