DEV Community

Cover image for When GitHub Action no longer works. Integrate SAST and DAST in shell scripting.
Charles Otugeh
Charles Otugeh

Posted on

When GitHub Action no longer works. Integrate SAST and DAST in shell scripting.

Securing modern web applications requires a holistic approach. Relying solely on static code analysis leaves you blind to runtime anomalies, while testing only live environments guarantees that minor coding mistakes turn into expensive, post-deployment hot fixes. 

To bridge this gap, top engineering teams combine Static Application Security Testing (SAST) and Dynamic Application Security Testing (DAST) inside their continuous integration pipelines. By automating this dual-layer defense using GitHub Actions, you can seamlessly validate code quality and runtime behavior on every single pull request. 

The Dual-Layer Defense: Inside-Out vs. Outside-In

Before diving into the YAML configurations, it is critical to understand how these two testing methodologies complement one another: 

  • SAST (Static Analysis): This approach acts from the "inside-out". It parses raw source code, binaries, or bytecodes without executing the application. It is highly efficient at detecting syntax flaws, hardcoded credentials, SQL injections, and outdated dependencies early in the Software Development Life Cycle (SDLC).
  • DAST (Dynamic Analysis): This approach acts from the "outside-in". It requires a fully running version of your application. DAST acts like a simulated attacker, throwing malicious payloads at active endpoints, APIs, and input fields to detect runtime infrastructure flaws, misconfigured HTTP headers, and authentication bypasses.

Designing the GitHub Actions Pipeline

To successfully deploy both scanners without bloating your execution times, structure your pipeline into a clear multi-stage workflow: 

  1. Code Commit & Pull Request(PR): Triggered immediately whenever code is pushed or a Pull Request is opened.
  2. The SAST Stage: Runs code scans in parallel with your build processes. If severe flaws are discovered, the pipeline fails early.
  3. The Spin-Up Stage: Launches a lightweight, containerized instance of the application in the runner environment.
  4. The DAST Stage: Points an automated scanner at the local containerized application, tests the endpoints, and uploads the security logs.

The Production Blueprint: .github/workflows/security.yml

Below is example a complete, production-ready Shell scripting an alternative to GitHub Actions workflow file. It utilizes Semgrep for lightning-fast SAST scanning and OWASP ZAP (ZED Attack Proxy) to perform the dynamic DAST analysis against a containerized instance of your app. to configure the shell scripting on your machine make it executable by running on CLI chmod +x [name of the file] then run ./local.sh

#!/usr/bin/env bash

Exit immediately if a command exits with a non-zero status

set -e

echo "========================================="
echo " Starting Local CI Security Pipeline"
echo "========================================="

----------------------------------------------------

STAGE 1: Local SAST Scan (Semgrep)

----------------------------------------------------

echo -e "\n Running SAST Analysis via Semgrep..."

Mount current directory into the Semgrep Docker container

docker run --rm -v "$(pwd):/src" returntocorp/semgrep semgrep scan \
--config=auto \
--error

echo "SAST Scan Completed Successfully!"

STAGE 2: Spin up local application container

echo -e "\n Building and starting local application container..."

Build your app image

docker build -t local-test-app .

Run the app container in the background on port 8080

docker run -d -p 8080:8080 --name running-local-app local-test-app

Ensure we clean up the container even if the DAST scan fails

cleanup() {
echo -e "\n Cleaning up test containers..."
docker stop running-local-app || true
docker rm running-local-app || true
}
trap cleanup EXIT

CRUCIAL: Wait for the local server to actively accept traffic

echo " Waiting for application to respond on port 8080..."
timeout 30s bash -c 'until $(curl -sSf http://localhost:8080 > /dev/null 2>&1); do sleep 2; done'

STAGE 3: Local DAST Scan (OWASP ZAP)

echo -e "\n Running DAST Analysis via OWASP ZAP Baseline..."

Note: We use --network="host" so the container can reach http://localhost:8080

docker run --rm --network="host" -v "$(pwd):/zap/wrk/:rw" -t ghcr.io/zaproxy/zaproxy:stable zap-baseline.py \
-t http://localhost:8080 \
-g gen.conf \
-r zap_report.html || DAST_EXIT_CODE=$?

Handle ZAP exit codes (ZAP returns 1 on errors, 2 on warnings)

if [ "${DAST_EXIT_CODE:-0}" -ne 0 ]; then
echo " DAST Scan detected vulnerabilities (Exit Code: $DAST_EXIT_CODE)."
echo " Check 'zap_report.html' in your directory for details."
exit $DAST_EXIT_CODE
else
echo "DAST Scan Completed Successfully!"
fi

echo -e "\n========================================="
echo "All local security checks passed!"
echo "=========================================

Strategic Best Practices for Smooth Execution

To prevent your automated security checks from frustrating your development team, apply these three core integration principles: 

1. Always Wait for the App to Wake Up

A common pitfall with DAST automation is failing the pipeline because the scanner targeted a dead port. Always incorporate a health check loop (curl -sSf) directly after starting your Docker container to ensure your app is fully operational before launching OWASP ZAP. 

  1. Manage False Positives Aggressively

Security scanners can be noisy. Use inline rule suppression files (like .semgrepignore or a ZAP rules.tsv file) to fine-tune your alerts. Start your pipeline in a warning-only configuration, monitor the trends, and flip the enforcement triggers (fail_action: true) to active only after weeding out initial false positives. 

3. Establish Clear Quality Gates

Do not force engineers to dig through cluttered workflow text output. Leverage standard GitHub capabilities by using the upload-sarif action. This native tool automatically maps your security scanner alerts directly into the GitHub Code Scanning Alerts dashboard and embeds clear, actionable inline annotations right inside your pull requests. 

Conclusion

Integrating SAST and DAST into local.sh file shifts your security operations left without adding friction to the developer experience. By inspecting code at rest and probing applications at runtime, you build a resilient, fully automated guardrail that catches vulnerabilities before they ever reach your staging or production environments. The only disadvantage here is that each developer in the team must make pull request and configure it locally on their machine.

Top comments (0)