How to Integrate testssl.sh into CI/CD Pipelines for Automated TLS/SSL Testing
Integrate testssl.sh into CI/CD pipelines by adding a single shell step that executes the Bash scanner with --fast --json flags, either by checking out the drwetter/testssl.sh repository directly or running its official Docker image.
The drwetter/testssl.sh repository provides a pure-Bash TLS/SSL scanner designed for command-line automation. Because it requires only standard Unix tools and OpenSSL, you can integrate testssl.sh into CI/CD pipelines without installing heavyweight agents or persistent services. Its single-file entry point and pluggable JSON output make it ideal for security gates in GitHub Actions, GitLab CI, and Jenkins.
Why testssl.sh Fits Modern CI/CD Workflows
The scanner’s architecture is intentionally lightweight for containerized environments. The main testssl.sh script serves as the sole entry point, parsing arguments and orchestrating tests through helper scripts located in the utils/ directory (written in Bash, Perl, and Java). Output modules support CSV, JSON-v1, JSON-v2, and HTML formats, selectable via --output flags or convenience options like --json. Because the tool runs entirely in user-space with no persistent service, it integrates seamlessly into ephemeral CI runners.
GitHub Actions Integration
The repository ships production-ready workflows in .github/workflows/unit_tests_ubuntu.yml and .github/workflows/unit_tests_macos.yml that demonstrate dependency installation and test execution. You can adapt these patterns to scan your own services.
name: TLS Scan
on:
push:
branches: [ main ]
pull_request:
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: drwetter/testssl.sh
ref: 3.3dev
path: testssl
- name: Run testssl.sh
run: |
cd testssl
./testssl.sh --fast --json result.json example.com:443
- uses: actions/upload-artifact@v4
with:
name: tls-report
path: testssl/result.json
For a ready-made wrapper, leverage the official testssl-sh-scan GitHub Action available in the marketplace, which encapsulates the same logic shown above.
Docker-Based Integration for GitLab and Jenkins
The Dockerfile in the repository root builds a multi-stage image bundling the scanner and a recent OpenSSL binary. This eliminates version skew when your CI runner’s host OpenSSL is outdated.
GitLab CI Example
tls_scan:
image: docker:latest
services:
- docker:dind
script:
- docker build -t testssl:dev .
- docker run --rm -v $CI_PROJECT_DIR:/output testssl:dev ./testssl.sh --fast --json /output/result.json example.com:443
artifacts:
paths:
- result.json
Jenkins Declarative Pipeline
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git url: 'https://github.com/drwetter/testssl.sh.git', branch: '3.3dev'
}
}
stage('Build') {
steps {
sh 'docker build -t testssl:dev .'
}
}
stage('Scan') {
steps {
sh '''
docker run --rm \
-v $WORKSPACE:/work \
testssl:dev \
./testssl.sh --fast --json /work/result.json example.com:443
'''
archiveArtifacts artifacts: 'result.json', fingerprint: true
}
}
}
}
If your runner lacks Docker, execute ./testssl.sh directly after checking out the repository; the script requires only Bash, OpenSSL (or LibreSSL), and standard Unix tools.
Implementing Security Gates with JSON Output
Use the --json flag to generate machine-readable output, then parse results with jq to enforce quality gates. The --fast flag disables time-consuming checks while retaining protocol and cipher suite validation, keeping CI execution times minimal.
./testssl.sh --fast --json result.json example.com:443
if jq -e '.[] | select(.severity == "HIGH")' result.json > /dev/null 2>&1; then
echo "Critical TLS vulnerabilities detected"
exit 1
fi
To use a custom OpenSSL binary—such as the one bundled in the repository—set the OPENSSL_BIN environment variable before invocation:
export OPENSSL_BIN=$PWD/bin/openssl.Linux.x86_64
./testssl.sh --fast --json result.json example.com:443
Summary
- Single entry point: The
testssl.shscript at the repository root drives all scanning logic, with helper utilities inutils/handling specific tests like Heartbleed or TLS-FALLBACK. - Native CI templates: Reference
.github/workflows/unit_tests_ubuntu.ymland.github/workflows/unit_tests_macos.ymlfor official dependency installation and testing patterns. - Containerized consistency: The
Dockerfileensures reproducible results across GitHub Actions, GitLab CI, and Jenkins by bundling a specific OpenSSL version. - Automated enforcement: Combine
--jsonoutput withjqparsing to fail pipelines on high-severity findings without manual review.
Frequently Asked Questions
Can I run testssl.sh without Docker in CI?
Yes. Because testssl.sh is a pure Bash script with dependencies limited to OpenSSL and standard Unix tools, you can execute it directly on Ubuntu, macOS, or Alpine runners. Simply check out the drwetter/testssl.sh repository and run ./testssl.sh from the working directory.
How do I fail a CI pipeline when testssl.sh finds critical vulnerabilities?
Run the scanner with the --json flag to generate structured output, then use a JSON parser like jq to check for high-severity entries. If the query returns matches, exit the shell step with a non-zero status code to mark the pipeline stage as failed.
Does testssl.sh support parallel scanning in CI environments?
While the script itself runs single-threaded per target, you can parallelize execution by launching multiple CI jobs or container instances simultaneously. Each instance should write to a unique output file to avoid collisions, and you can aggregate results in a final pipeline stage.
Which output format is best for programmatic CI integration?
JSON is the recommended format for CI/CD automation because it provides structured severity ratings and finding identifiers that your pipeline can parse with standard tools. Use --json for the standard JSON format or consult doc/testssl.1.md for additional output options including CSV and HTML.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →