Integrating SAST (Static Application Security Testing) into a CI/CD pipeline

Vulnerabilities are cheaper to fix before release than to deal with the aftermath of an incident. We embed static analysis into the build end to end: we configure rules for your code, cut false positives, and hand the team ready-to-use policies. Defect triage is reduced threefold, and releases go out on schedule.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

SAST in the pipeline: why vulnerabilities reach release

Static code analysis most often lives on a separate island: it runs on a schedule or manually, results go into a report that few open. The build pipeline passes fully green — none of its steps looks at what changes got into it.

Therefore vulnerabilities in code surface not where they appeared. Unvalidated input going straight into a query, or an access key committed next to a config, safely reach the test environment and sometimes production.

They are found more often not by the development team but by a perimeter scanner or an external audit report.

The cost of a finding is not the fix itself but the associated work: determine which version is currently in production, who authored the change, whether the defect falls under audit requirements, and manage to ship an unscheduled release.

While this happens, engineers reconstruct context from other people's changes instead of their tasks — one finding takes a day or two of several people's work. If the defect is in a library pulled by several other services, the count is in releases.

SAST changes the picture not by the fact of running but by the place of verification in the process: static analysis becomes a pipeline step that stops the build when a finding of a given class occurs and leaves a trace in the change history. We analyze how to embed code verification before release without losing build speed.

What changes after embedding checks into the build

Code checks (SAST) are embedded at two points: parsing changed files locally before sending and a full pass on each branch in the build. Critical findings block merging into the main branch; the rest go into a report linked to the line and defect class from the OWASP list.

The reviewer stops being the only filter: they look at logic rather than searching for a forgotten access control check.

The share of defects found before review grows from a quarter to 70–80%: unescaped input, a secret in a config, and insecure deserialization are caught at build time, not during testing.

Code that has already passed a formal barrier comes to review, and discussion shifts to architecture.

Finding triage time is reduced because the report provides context — file, line, rule, fix example. Triage takes minutes instead of half an hour of searching through changes, if false positives are suppressed by targeted configuration rather than disabling the rule entirely.

The number of incidents after rollout drops severalfold: entire classes of problems do not reach the main branch.

Unified analysis rules set the same strictness threshold for all repositories — a new service is checked from day one just like an old one, and no project with a weakened rule set appears.

Code quality grows without a one-time crunch: a baseline level of found issues is fixed, and the build fails only on new ones. Old findings are closed as edits are made, so checks in the build do not slow down release.

SAST launch options: build, changes, schedule

SAST is rarely enabled at one point in the pipeline: different modes cover different risks and affect build time differently.

Incremental analysis gives the change author feedback in minutes; a full code run catches inter-module data flows that are invisible when checking individual files.

Mode When it fits Limitations
Checking changed files on every pull request Fast feedback before review is needed Does not see connections to the rest of the codebase, produces more false positives
Full code run in the build Small repositories, critical releases Lengthens the pipeline and loads build agents
Nightly scheduled build Large and multi-language projects Findings arrive with a delay, and triage is needed in the morning
Manual run on the release branch Preparing for release, incident triage Regularity relies on team discipline

In practice, modes are combined: checking changed files is made a mandatory step on every pull request, the nightly build covers the entire codebase, and build failure is configured only for high-severity findings.

Known triggers are moved to a baseline file, otherwise the report quickly loses value and stops being read.

We deliver the report in SARIF format directly to the review system — the comment is visible in the diff, without a separate portal. Next we will analyze which set of modes is appropriate for your build time and project language, and fix thresholds in pipeline rules.

Step-by-step implementation: from inventory to handover

SAST implementation is a sequence of work where each stage relies on the result of the previous one, not the installation of a single analyzer.

We start with repository inventory: without a map of services, languages, and branches, it is impossible to configure rules so that checks do not turn into a stream of false positives that the team disables within a week.

  1. Repository inventory. We collect a list of projects, languages, build tools, and active branches, and mark archival ones. We record where linters and other checks already operate so as not to duplicate signals.
  2. Integration scheme. We determine launch points: on every change or only before merging into the main branch. Here we also agree on what to do when a finding occurs — block merging or warn for now.
  3. Profile configuration. For each language and service type — its own rule set. Critical ones (injections, secret leaks, insecure file handling) are enabled immediately, noisy rules — in observation mode, and for old projects we set a baseline so as not to triage accumulated debt all at once.
  4. Validation on a real build. We run analysis on several production repositories: measure how much time it adds to the build, cut false positives, check the readability of the report and code links.
  5. Rollout by groups. We connect the remaining repositories in waves — from simple services to those related to payments and personal data. Each wave is accompanied by triage of findings together with code authors.
  6. Handover to the team. We provide the finding triage regulations, the procedure for updating rules, and on-call duties. We separately record how to temporarily weaken a check — with a mandatory return date.

Configurations, rules, and regulations: what we hand over

After SAST implementation, the customer receives not a report on a one-time check but working artifacts in their repository: they are edited, transferred between projects, and handed over to new engineers without our involvement. Below is the list of what remains with you, with an explanation of why each element is needed.

  • Configuration of the check stage in the pipeline file: triggers for merge requests and nightly builds, blocking threshold, timeouts, cache of intermediate results.
  • Rule sets for your stack: a base profile plus separate profiles for critical services with branch binding.
  • Analysis profiles: parsing depth, target directories, exclusions for generated code and external libraries — each with justification.
  • Response regulations: severity categories, remediation timelines, who triages a finding, when the build is blocked, and how a justified rejection is documented.
  • Finding flow: from commit to report, where results are stored and how they are linked to tasks in the tracker.
  • Check documentation: how to add a rule, how to suppress a false positive, how to update profiles when the stack changes.
  • Developer instructions: local run before sending changes, report triage, typical remediation scenarios.
  • Rule change log and responsibility matrix: who approves new exclusions and who is responsible for reviewing profiles.

Case: build checks reduced defect triage threefold

What was before build checks

The environment has three products with a shared library and separate repositories. Static analysis was run manually before release: findings accumulated for months and were triaged in batches; one wave took up to two weeks of engineering time.

Half of the comments related to code that would be rewritten anyway — the useful signal drowned in noise, and edits after review dragged out release timelines.

How thresholds were configured

SAST was deployed directly in the build pipeline based on SonarQube and a baseline was fixed for the current code state: old findings went into the report but did not block the build — otherwise teams would have stalled on day one.

The quality threshold was applied only to new changes and edits in affected files, and rules were split into three levels: blocking, warnings, and informational. Generators, migrations, and test fixtures were moved to exclusions so that the report did not lose trust.

Over three months, the share of defects reaching release decreased threefold, finding triage time per change — from 40 to 12 minutes, and the share of new edits under checks grew to 90%.

The report in the change description removed a separate approval stage: the author sees the cause before review and closes it themselves.

SAST or dynamic analysis: what to choose at the start

SAST and dynamic analysis answer different questions.

Static parsing goes through source code and does not run the application: it builds a call graph and tracks data flow from entry points to dangerous operations — this is how injections, authorization bypass, insecure deserialization, and cryptographic errors are found. It sees code that is not yet covered by tests, and fits into minutes at the branch merge stage.

Dynamic analysis looks at an already running application: it injects data into real requests and records what manifested at runtime — configuration errors, extra headers, access to other users' objects.

There are fewer false positives here, but coverage is limited by the scenarios you ran and the code those scenarios reached. Therefore the approaches do not replace but complement each other.

The third layer is dependency checking: manifests and lock files are compared against databases of known vulnerabilities, including transitive libraries, and licenses are checked separately.

It is enabled faster than the others, does not require a full project build, and provides a noticeable share of real findings, because a significant part of the code comes from outside.

It is reasonable to start with dependencies: minimal cost of enabling in the build pipeline and predictable results.

Next — SAST on changed code, incrementally, by branch diff, so the team does not drown in triggers; when false positives are marked and the criticality threshold is agreed, the check is extended to the entire repository and made blocking.

Dynamic analysis is connected when there is a test environment with stable data and repeatable scenarios. One configured tool with a triaged trigger base is more useful than three enabled formally.

Common doubts: false positives and build speed

Engineers' doubts around SAST usually come down to four things: how much noise will arrive in the report, what will happen to the build blocking threshold, whether the check will fit into the pipeline, and what to do with legacy code where thousands of comments have accumulated. We address each one — and how it is resolved in practice.

The general principle is not to enable the check "for everything at once," but first to fix a baseline snapshot: current findings are marked as known debt, and only new ones become blocking. This mode gives the team a clean signal from day one and does not stop releases.

Will the build fail because of false positives?

The blocking threshold is configured by rule class and confidence level: only confirmed vulnerabilities in changed code stop the build; disputed findings go to the report without blocking.

Each exclusion is documented as a rule with a reason and review date — noise does not accumulate silently.

How much will the check slow down?

Analysis runs on changed files and their dependencies, not the entire codebase, and results are cached between builds. A full deep pass is moved to a separate schedule. For a typical build, this adds minutes, not tens of minutes, and does not block parallel tasks.

We have thousands of old comments — where to start?

Legacy code is triaged by baseline snapshot: we fix the current volume, break it down by criticality and modules, then a strict threshold applies only to new code. Old findings are closed on a planned basis — primarily on hot paths and at the perimeter, where risk is higher.

Unified rules remain common to all teams, and differences are documented as profiles and local policies, not by disabling checks.

Let's discuss the task: implementation points and order of steps

So that the assessment and plan rely on facts, not assumptions, before work starts we fix the implementation points and order of steps.

Below are the questions we resolve in the first conversation: the answers determine the scope of work and the sequence in which SAST is embedded in the existing build pipeline.

  • Where we place the check: build step, pre-commit hook, image scanning before publishing to the registry, or a combination of points.
  • The primary language and build tool, whether there is code generation and dependencies that are built only on the build agent.
  • Start mode: report without blocking the build or stopping when the criticality threshold is exceeded.
  • Who owns exclusion rules and how a false positive is confirmed — otherwise the rule will be disabled entirely.
  • How findings reach the developer: a comment in the task, a report as a build artifact, a summary by release.
  • Which branches are protected and what happens when a check fails — does the build stop or continue with a note.
  • What is required for finding history and report access, whether export to an external system is needed.
  • Acceptance criteria: share of checked changes, time to first finding, how many critical defects reach the test environment.

Describe the task and how your pipeline is currently arranged — we will return with an assessment and plan: implementation points, order of steps, timelines, and acceptance criteria.