Configuration, findings triage and false positive handling
Static code analysis and dependency checking are among the security features GitLab ships built in, and they can be wired into an existing .gitlab-ci.yml with just a few lines. The real work starts only afterwards: results need to be assessed, real vulnerabilities need to be prioritized, and false alarms need to be suppressed cleanly, without the security noise eroding developer trust in the tooling. This article covers integrating SAST and Dependency Scanning into an existing PHP or Node pipeline, and the practical handling of the findings that come out of it.
Table of Contents
- 1. What SAST and Dependency Scanning Each Actually Do
- 2. Wiring SAST Into an Existing Pipeline
- 3. Wiring In Dependency Scanning for PHP and Node
- 4. How GitLab Presents and Prioritizes Findings
- 5. From Finding to Resolved Issue: A Practical Workflow
- 6. Dealing With False Positives
- 7. Controlling Pipeline Behavior With Thresholds
- 8. Impact on Pipeline Runtime and Resource Usage
- 9. Best Practices for Sustainable Operation
- 10. Summary
- 11. FAQ
1. What SAST and Dependency Scanning Each Actually Do
Static Application Security Testing, or SAST, analyzes your own source code without executing it and looks for known insecure patterns such as unsanitized SQL queries, unsafe deserialization or hard coded credentials. Dependency Scanning, on the other hand, does not look at your own code but at the list of included third party packages, for instance from composer.lock or package-lock.json, and checks it against a database of known vulnerabilities. The two analyses complement each other, because a large share of real world security vulnerabilities today sit in dependencies rather than in hand written code.
GitLab ships ready made CI/CD templates for both analysis types, included via include:template, which automatically pick the right analyzers for the languages detected in the repository. For a PHP codebase this means semgrep as the SAST analyzer and gemnasium-php as the dependency scanner, for Node.js correspondingly semgrep and gemnasium-nodejs. This automatic detection removes a lot of manual configuration work, but only works reliably when the project structure follows the usual conventions of the respective language.
2. Wiring SAST Into an Existing Pipeline
The base integration is deliberately minimal: a single include:template entry for Security/SAST.gitlab-ci.yml is enough to automatically add the matching analyzer jobs to the pipeline. GitLab internally detects the languages present in the repository and only activates the analyzers that are actually relevant, so a pure PHP project does not end up running an unnecessary Java or Go scan alongside it. The results are stored as a SAST report artifact at the end, which GitLab automatically renders in the merge request widget as a list of security findings.
For existing, already grown pipelines it matters that the SAST job runs in the test stage by default. If that stage does not exist in your own .gitlab-ci.yml or is named differently, you either need to add test to your own stage list or explicitly assign the SAST job to an existing stage. A frequent stumbling block is also that SAST runs noticeably longer than the rest of the pipeline on very large monorepos, which is why a dedicated, parallel stage for security jobs often pays off, so the overall runtime is not extended unnecessarily.
# .gitlab-ci.yml
stages:
- build
- test
- security
- deploy
include:
- template: 'Security/SAST.gitlab-ci.yml'
semgrep-sast:
stage: security
3. Wiring In Dependency Scanning for PHP and Node
Dependency Scanning is included the same way via include:template with Security/Dependency-Scanning.gitlab-ci.yml, and automatically detects manifest files such as composer.lock, package-lock.json or yarn.lock. For PHP projects, composer install needs to have run before the scan job so the full dependency tree, including transitive packages, is available, otherwise the scanner only analyzes an incomplete subset. For Node projects the equivalent applies: npm ci or yarn install should run beforehand so the lockfile version matches what is actually installed.
An important practical difference from SAST is that Dependency Scanning depends on an externally maintained, regularly updated vulnerability database. New vulnerabilities in already included packages can therefore surface even if nothing in your own code has changed for weeks, simply because a new CVE was published for an existing dependency. This is exactly why combining it with a Scheduled Pipeline makes sense, checking for new findings regularly regardless of code changes.
# .gitlab-ci.yml
include:
- template: 'Security/Dependency-Scanning.gitlab-ci.yml'
gemnasium-php-dependency_scanning:
stage: security
before_script:
- composer install --no-dev --prefer-dist
4. How GitLab Presents and Prioritizes Findings
Every reported finding automatically gets a severity rating from Info through Low, Medium and High to Critical, based on the CVSS score of the underlying vulnerability or the rule classification of the SAST analyzer. In the merge request widget, new findings appear directly next to the affected line of code or package, so reviewers see them without opening a separate report. The security dashboard at the project and group level additionally aggregates all findings over time and makes trends visible, for example whether the count of open high severity findings is rising or falling.
For day to day prioritization it works well to focus exclusively on Critical and High findings first, and to handle Medium and Low findings in regular but less frequent reviews, for instance monthly instead of on every merge request. Without this prioritization a team quickly risks drowning in a flood of findings, many of which have limited real world impact, while the genuinely critical ones get lost in the noise.
5. From Finding to Resolved Issue: A Practical Workflow
A proven workflow starts by converting every Critical or High finding directly from the security dashboard into a GitLab issue, which GitLab allows in a single click and which automatically carries all relevant metadata like the affected file, line number and CVE reference. That issue then goes through the same prioritization and assignment process as any other bug, instead of disappearing into a separate, often neglected security list.
For Dependency Scanning findings, the fix is often a simple version bump of the affected dependency, achievable with composer update package-name or npm update package-name, followed by another pipeline run to confirm. For SAST findings in your own code, an actual code change is usually necessary, for example moving from unsafe string concatenation in a SQL query to a parameterized statement, which typically deserves its own merge request with review rather than a quick patch without a second pair of eyes.
6. Dealing With False Positives
No automated scanner is perfect, and both SAST and Dependency Scanning occasionally produce findings that, on closer inspection, are not real vulnerabilities. A classic example is a SAST finding for a supposedly unsafe function that in the specific context is already secured by upstream validation, or a dependency finding for a package whose vulnerable code path is not even used in the project. Simply marking such cases as resolved without documenting them leads the scanner to raise the exact same alarm again on the next run.
GitLab's security dashboard offers a way to explicitly mark a finding as a false positive, including a mandatory field for the justification. That marking is stored permanently and prevents the same finding from showing up as open again in future scans, as long as the underlying code or dependency version does not change. It is important to actually document these justifications carefully, since a later review by a security auditor otherwise has no way to understand why a finding was classified as non critical.
# .gitlab-ci.yml: excluding a path from SAST
variables:
SAST_EXCLUDED_PATHS: "spec, test, tests, tmp, vendor/legacy-lib"
7. Controlling Pipeline Behavior With Thresholds
By default, a security finding does not automatically block the pipeline, it merely appears as information in the merge request widget, so developers are not blocked by every single low severity finding on every merge. For stricter requirements, a so called scan result policy can be defined via security policies at the group or project level, which for example automatically blocks any merge with an unresolved critical finding until either a fix or an approved exception is in place.
These policies can be configured granularly, for instance only applying to certain branches such as main, while feature branches remain free to develop against. In regulated environments where compliance requirements demand a documented approval for every known vulnerability, such a policy is often indispensable, while smaller teams frequently get by well with pure visibility in the merge request widget, without introducing hard blocks.
8. Impact on Pipeline Runtime and Resource Usage
SAST and Dependency Scanning jobs require their own, sometimes fairly memory hungry analyzer containers, which can noticeably extend a pipeline's overall runtime, especially on large codebases. In practice it has proven effective to place these jobs in a dedicated stage that runs in parallel with test jobs, rather than sequentially after the functional tests, so test and security runtime overlap instead of adding up.
For very large repositories it also helps to run Dependency Scanning not on every push, but only on merge requests against the main branch, plus additionally at night via a Scheduled Pipeline, as described in the earlier article on scheduled pipelines. This noticeably reduces CI minute consumption without compromising security, since new vulnerabilities in existing dependencies need to be detected regardless of when a commit happens anyway.
9. Best Practices for Sustainable Operation
Sustainable security scanning operations require clear responsibilities: who reviews new findings, who decides on false positive markings, and who escalates critical findings outside the normal sprint cadence. Without this clarity, findings become orphaned in the dashboard and the initial investment in the integration fizzles out, because nobody actually works through the reported problems.
Just as important is regularly watching the false positive rate: if it rises noticeably over time, that often points to an overly aggressive analyzer configuration, which can be reduced by targeted use of SAST_EXCLUDED_PATHS or by disabling individual rules that are irrelevant to the specific project. The table below compares the key properties of SAST and Dependency Scanning to clearly distinguish the two analysis types from each other.
| Property | SAST | Dependency Scanning |
|---|---|---|
| Analyzes | Your own source code | Included third party packages |
| Data source | Static analyzer rules | External vulnerability database |
| Changes without code change | No | Yes, when a new CVE affects an existing package |
| Typical fix | Code change with review | Version bump of the dependency |
| Recommended cadence | On every merge request | Merge request plus nightly scheduled pipeline |
Mironsoft
CI/CD pipelines, zero-downtime deployments and release automation
Deployments that run without downtime and without the nail-biting?
We review existing GitLab pipelines for fragile deployment steps and missing safeguards, then build a release process with zero-downtime deployments, automated checks and a rollback you can actually trust in an emergency.
Pipeline Review
Checking an existing .gitlab-ci.yml for fragility, missing stages and security gaps.
Zero-Downtime Deployment
Building symlink releases, health checks and rollback strategies for Magento stores.
CI/CD Automation
Connecting tests, security scans and deployments into one reliable pipeline.
10. Summary
GitLab SAST and Dependency Scanning: Key Takeaways
Two analysis types
SAST checks your own code, Dependency Scanning checks third party packages against a vulnerability database.
Minimal integration
A single include:template entry is enough, GitLab detects languages and matching analyzers automatically.
Triaging findings
Convert Critical and High into issues with priority, handle Medium and Low in regular reviews.
Document false positives
Explicit marking in the security dashboard with a justification instead of silently ignoring them.