hift-left in practice: the implementation of SAST and DAST in CI/CD without disruption of releases

Depov

Activist
ULTIMATE
SUPREME
PREMIUM
MEMBER
Joined
Feb 18, 2025
Messages
126
Reaction score
115
Deposit
0$
CI/CD-pipeline as the surface of the attack: why does it be known to the pentester
Before you build scanners, it is worth looking at the pipeline through the attacker’s eyes. CI/CD is a full-fledged surface of the attack with its own TTPs in MITRE ATT&CK. More details - in our Detailed analysis of the attack on the supply chain.

Poisoned Pipeline Execution (T1677, Execution) - the attacker modifies the CI-configuration (.gitlab-ci.yml, Jenkinsfile, GitHub Actions bluflow) to perform arbitrary code in the context of runner. If runner has access to production secrets, this is a direct path to compromising the infrastructure. Any developer with the right to merge request can potentially change the pipeline. It sounds harmless until you remember that runners often spin with the same creeds as the decks.

Compromise Software Dependencies and Development Tools (T1195.001, Initial Access) Supply chain through malicious dependence. Typosquatting in PyPI or NPm, compromised continent, malware in transitive third-level dependence. OWASP is A06:2021 (Vulnerable and Outdated Components). After Log4Shell and XZ Utils SCA-scanning from the "desirable" became mandatory - there are no options.

Credentials In Files (T1552.001, Credential Access) - API keys, tokens, passwords, random in code or config. Get into the Git history and remain available even after removal from HEAD. One AWS key in a public repository - and after minutes cryptominers begin to spin. I saw the key to S3 leaked through .envwhich “accidentally” got into the commission.

Exploit Public-Facting Application (T1190, Initial Access) - what DAST simulates: injection attacks, XSS, SSRF, misconfiguration. According to OWASP, it is A03:2021 (Injection) and the associated risks.

Code Repositories (T1213.003, Collection) - repository as a source of data for the attacker: documentation, internal API-endpoints, database schemes, comments with TODO like "remove the password hardcode before release." Such TODO is a gift for intelligence.

Shift-left security simultaneously reduces the surface of the attack of the application and protects the pipeline itself. For a pentester, this means: if DevSecOps-pipeline is competently configured on the project, low-hanging fruit in the form of typical SQLi and XSS is already filtered. The value of manual testing is in business logic, vulnerabilities and authorization bypass. According to a study by totalshiftleft.com, the vulnerability that costs $ 500 at the code spelling stage can cost $50 000+ after hitting the sale - taking into account the response incident, emergency patching and user notifications.
Static and Dynamic Analysis Tools: Trade-off Table
The main criterion for choosing SAST tools for developers is not the power of the scanner, but the ratio of speed, accuracy and cost of support. Too slow the scanner will be turned off. Too noisy, they'll ignore. I've seen it dozens of times.
1782072640239.png

[Applicable: Internal DevSecOps, all types of projects. For external pentest - Nuclei and ZAP are used directly, without CI-coupation]

All the tools in the table are open-source and actively supported by: Semgrep - Semgrep Inc (formerly r2c), Trivy - Aqua Security, ZAP - OWASP Foundation, Nuclei - ProjectDiscovery, Bandit - PyCQA. For enterprise scenarios, there are commercial alternatives: Checkmarks SAST, Veracode, PT Application Inspector from Positive Technologies - with advanced reporting and management of false positive-leveling.
Step-by-step implementation of SAST and DAST in pipeline
Adjustments to the environment
• CI/CD: GitLab CI/CD with Docker-executor (examples below for GitLab; for GitHub Actions logic is the same, the syntax .yml is different)
• Runner: at least 2 GB RAM, recommended 4 GB for parallel securityjob
• Docker: to run containerized scanners (ZAP, Trivy, Semgrep)
• Staging: an environment available from runner on HTTP/HTTPS, for DEST scanning
• Semgrep CLI v1.x: free, pip install semgrepor Docker image semgrep/semgrep:latest
• OWASP ZAP: Docker Image ghcr.io/zaproxy/zaproxy:stable
• Trivy v0.50+: Docker image aquasec/trivy:latestor system installation
• Network: runner must have access to registry.semgrep.dev (to upload rules) and staging-URL
Step 1: Pre-commit - intercepting secrets to Git history
Pre-commit is the earliest shift-left security frontier. Gitelaaks in pre-commit hoke will not allow the developer to compromise the AWS-key, JTT token or password from the database. Bandit in the same pre-commit will catch dangerous Python structures: eval(), pickle.loads(), subprocess.call(shell=True).

Configuring through .pre-commit-config.yaml with Bennit custom configuration pyproject.toml (section [tool.bandit]) allows you to cut false positive for a particular project - for example, tests that intentionally use dangerous structures. This is a direct reaction to the Credentials In Files technique (T1552.001): the secrets are intercepted before hitting the Git story.

Restriction: pre-commit works on a local machine. If the developer has not installed hooks or threw a business through --no-verify - Findings will go to the repository. Therefore, duplication of Giteaks and Bandit in CI/CD Jobs is mandatory. This is insurance against the human factor, and without it the whole structure crumbles.
Step 2: SAST in diff mode for every erge request
Principle: scan only modified files, block Critical/High only. The full scan of the entire codebase is for the scheduled night-based.
YAML:
semgrep-sast:
stage: test
image: semgrep/semgrep:latest
script:
- semgrep ci --config auto --severity ERROR
--json -o semgrep.json
artifacts:
paths: [semgrep.json]
when: always
rules:
- if: $CI_MERGE_REQUEST_IID
--config auto Connects the community-rules Semgrep, optimized for security. --severity ERROR - pipeline falls only on Critical/High. Please note: no allow_failure: true. If Semgrep has found an SQL injection or hardcoded secret - merg request is blocked before the correction. Goba is tied to a mer hypermission through $CI_MERGE_REQUEST_IID It does not run on straight push.

For medium-finds add the second Jobu: the same Semgrep with --severity WARNING and allow_failure: true. Send results to Defect Dojo or Jira via API - this is a backlog of the technical debt on security that does not block the release, but is tracked. Without trekking, the minutes-finishes turn into “then we will understand”, and “then” never comes.

In parallel on stege security Start SCA-scanning: trivy image --exit-code 1 --severity CRITICAL,HIGH $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME. Trivy checks the Docker image for the well-known CVE in system packages and libraries - a layer that SAST does not really see. To check the entire repository (including files that are not included in the image at multi-stage build) trivy fs --exit-code 1 --severity CRITICAL ./ in a separate joo.
Step 3: DAST scanning on staging
The DAST starts after the deck - it needs a working application. This is a dynamic security test outside, essentially an automated recon.

Two modes, two schedules:

Baseline scan (passive, 2-5 minutes) - for each table in staging:
YAML:
zap-baseline:
stage: post-deploy
image: ghcr.io/zaproxy/zaproxy:stable
script:
- zap-baseline.py -t $STAGING_URL
-J report.json -l WARN
artifacts:
paths: [report.json]
when: always
rules:
- if: $CI_COMMIT_BRANCH == "main"
Baseline checks HTTP security headers (CSP, X-Frame-Options, HSTS), cookies (Secure, HtpOnly, SamSite), information leaks (server versions, stack traces). XSS or SQLi it won't find - it takes an active scan.

Full scan (active, 20-60 minutes) - in a pipeline, at night or once a week. Replace zap-baseline.py on zap-full-scan.py. ZAP will send real payloads: SQL injections, XSS vectors, SSRF tests, authentication checks. Before the in-high releases, run manually and analyze the results with the team. This is the case when 40 minutes of waiting is justified.

Nuclei as an add-on: nuclei -u $STAGING_URL -t cves/ -t misconfigurations/ -severity critical,high takes 1-3 minutes and checks staging on known CVE in the technologies used (a particular version of Nginx, Redis, Elasticsearch). Templates are updated community ProjectDiscovery - coverage in thousands of inspections. Nuclei catches well what ZAP baseline misses: specific CVE for specific software versions. On one project, it was Nuclei who found an open Elastassearch 7.10 with CVE-2021-22145, which ZAP baseline did not catch.

DAST limit in CI/CD: Automatic DEST scan does not replace authenticated testing with multiple roles. The horizontal escalation (user A sees user B data) requires tuned auth-contexts in ZAP - this is a separate configuration task that goes beyond the basic implementation.
Security gates in pipeline: severity-threading without paralysis
Quality gate is the point where the pipeline passes or falls. Setting up the thresholds is the most politically loaded part of security integration into DevOps because it affects the speed of the entire team. Here you can not come and say "now block everything" - get sabotage.

The work strategy is a phased tightening. The model is based on the approach OWASP DSOMM (DevSecOps Maturity Model), which defines the categories Build, Test, Information Gathering and Culture to assess the maturity of DevSecOps processes.

Month 1-3: Advisory. All security-jobs with allow_failure: true. The results are visible in MR comments, do not block anything. The goal is to collect baseline: the average number of finds on MR, percentage false positive, what rules generate noise. Tuite ruleset: add exceptions, customize the Semgrep rules for codebase. If after three months false positive rate above 30% - do not go to block, continue tuning. Seriously, do not hurry. Premature blocking will kill the team’s trust in the tools.

Month 4-6: Blocking Critical/High. allow_failure: false on SAST and SCA-jobs for ERROR. Medium - in advisory. The team gets used to blocking and learns to correct the findings without escalation. Target false positive rate - below 20%.

Month 7+: Full gates. Critical/High is blocked by merge. Medium creates a teat in Jira/Defect Dojo with SLA for 30 days. DASST baseline is a mandatory step before production. Tracking tags: MTTR (average time before correction), escape rate (injury to the sale), false positive rate.

Key rule: Each alter must contain a specific recommendation. Semgrep allows you to add fix: and message: in custom rules - the developer sees not an abstract "potential SQL injection", but a specific parameterized request for replacement. The tool that generates hundreds of unexplained warnings will be shut down by the team in the first week. And rightly - such a tool does not increase security, but creates the illusion of the process.
 
Top Bottom