Skip to content

DevSecOps test

DevSecOps test #19

name: DevSecOps Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
# Required permissions for code scanning API and committing reports
permissions:
security-events: write
actions: read
contents: write # Upgraded from 'read' to 'write' to allow committing files
jobs:
secrets-scanning:
name: Secrets Scanning
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
# Create docs/reports directory if it doesn't exist
- name: Ensure docs/reports directory exists
run: mkdir -p ./docs/reports
- name: TruffleHog OSS
uses: trufflesecurity/trufflehog@main
with:
extra_args: --debug --json
# Generate TruffleHog report in docs/reports
- name: Generate TruffleHog report
run: |
echo "Running TruffleHog scan manually to save report"
docker run --rm -v $(pwd):/pwd trufflesecurity/trufflehog:latest github --repo file:///pwd --json > ./docs/reports/trufflehog-results.json || true
# Upload TruffleHog results as artifact
- name: Upload TruffleHog results
uses: actions/upload-artifact@v4
if: always()
with:
name: trufflehog-results
path: ./docs/reports/trufflehog-results.json
sast-scanning:
name: Static Application Security Testing (CodeQL)
runs-on: ubuntu-latest
needs: secrets-scanning
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ github.token }}
# Create docs/reports directory
- name: Ensure docs/reports directory exists
run: mkdir -p ./docs/reports
# Initialize CodeQL
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript
queries: security-and-quality
# Autobuild (attempts to automatically build any compiled languages)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# Run CodeQL Analysis - analyzes and produces SARIF file(s)
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "javascript-analysis"
output: ./docs/reports
# Copy and consolidate results to docs/reports directory
- name: Process CodeQL SARIF results
run: |
echo "Listing CodeQL SARIF files in ./docs/reports:"
find ./docs/reports -type f -name "*.sarif" | sort
# Check if any SARIF files were generated
if [ -n "$(find ./docs/reports -type f -name '*.sarif')" ]; then
# Create a consolidated sarif file for DefectDojo
echo "Creating consolidated SARIF file for DefectDojo"
# Find the first SARIF file and make a copy for DefectDojo
FIRST_FILE=$(find ./docs/reports -type f -name "*.sarif" | head -1)
echo "Using file: $FIRST_FILE for consolidated results"
# Make sure the file exists and is not a directory
if [ -f "$FIRST_FILE" ]; then
cp "$FIRST_FILE" ./docs/reports/codeql-results.sarif
# Display info about the consolidated file
echo "Consolidated SARIF file created at ./docs/reports/codeql-results.sarif"
ls -la ./docs/reports/codeql-results.sarif
echo "First 20 lines of consolidated SARIF file:"
head -n 20 ./docs/reports/codeql-results.sarif
else
echo "Warning: Selected file is not a regular file. Creating placeholder instead."
echo '{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "CodeQL",
"version": "placeholder",
"rules": []
}
},
"results": []
}
]
}' > ./docs/reports/codeql-results.sarif
fi
else
echo "No CodeQL SARIF files found, creating placeholder"
echo '{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "CodeQL",
"version": "placeholder",
"rules": []
}
},
"results": []
}
]
}' > ./docs/reports/codeql-results.sarif
fi
# CodeQL automatically uploads results, so we don't need this step.
# We're only keeping the artifact upload for DefectDojo integration
# Save SARIF as artifact
- name: Upload SARIF as artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: codeql-results
path: ./docs/reports/codeql-results.sarif
sca-scanning:
name: Software Composition Analysis
runs-on: ubuntu-latest
needs: sast-scanning
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ github.token }}
- name: Create reports directory
run: mkdir -p ./docs/reports
- name: OWASP Dependency-Check Scan - Backend
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'Angular-XSS-Backend'
path: './xss/api'
format: 'SARIF'
out: './docs/reports'
args: >-
--suppression ./dependency-check-suppressions.xml
--failOnCVSS 11
- name: OWASP Dependency-Check Scan - Frontend
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'Angular-XSS-Frontend'
path: './xss/frontend'
format: 'SARIF'
out: './docs/reports'
args: >-
--suppression ./dependency-check-suppressions.xml
--failOnCVSS 11
- name: Check SARIF files
run: |
echo "Checking if SARIF files exist in docs/reports directory"
if [ -d "./docs/reports" ]; then
ls -la ./docs/reports/
echo "Found files in docs/reports directory"
sarifCount=$(find ./docs/reports -name "*.sarif" | wc -l)
if [ "$sarifCount" -gt 0 ]; then
find ./docs/reports -name "*.sarif" | while read file; do
echo "Found SARIF file: $file"
echo "File contents (first 20 lines):"
head -n 20 "$file"
done
else
echo "No SARIF files found in docs/reports directory. Creating placeholder."
echo '{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "OWASP Dependency-Check",
"version": "placeholder",
"rules": []
}
},
"results": []
}
]
}' > ./docs/reports/dependency-check-placeholder.sarif
fi
else
echo "Reports directory does not exist!"
mkdir -p ./docs/reports
echo '{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "OWASP Dependency-Check",
"version": "placeholder",
"rules": []
}
},
"results": []
}
]
}' > ./docs/reports/dependency-check-placeholder.sarif
fi
- name: Find SARIF files
id: find-sarif
run: |
# Find all SARIF files
echo "Finding SARIF files in ./docs/reports directory"
find ./docs/reports -name "*.sarif" -type f
# Check if we have any SARIF files
SARIF_COUNT=$(find ./docs/reports -name "*.sarif" -type f | wc -l)
if [ "$SARIF_COUNT" -eq 0 ]; then
echo "No SARIF files found, creating placeholder"
echo '{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "OWASP Dependency-Check",
"version": "placeholder",
"rules": []
}
},
"results": []
}
]
}' > ./docs/reports/dependency-check-placeholder.sarif
echo "primary_sarif=./docs/reports/dependency-check-placeholder.sarif" >> $GITHUB_OUTPUT
else
# Get the first SARIF file for primary upload
PRIMARY_SARIF=$(find ./docs/reports -name "*.sarif" -type f | head -1)
echo "Found primary SARIF file: $PRIMARY_SARIF"
echo "primary_sarif=$PRIMARY_SARIF" >> $GITHUB_OUTPUT
fi
# Upload primary SARIF file
- name: Upload primary SARIF file
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: ${{ steps.find-sarif.outputs.primary_sarif }}
category: dependency-check
# Upload any additional SARIF files individually
- name: Upload additional SARIF files
run: |
# Skip the first file as it was already uploaded
COUNT=0
for sarif_file in $(find ./docs/reports -name "*.sarif" -type f); do
if [ $COUNT -gt 0 ]; then
echo "Uploading additional SARIF file: $sarif_file"
# Use the GitHub API to upload the SARIF file
UPLOAD_URL="https://api.github.com/repos/$GITHUB_REPOSITORY/code-scanning/sarifs"
curl -X POST \
-H "Authorization: Bearer ${{ github.token }}" \
-H "Accept: application/vnd.github.v3+json" \
-H "Content-Type: application/json" \
-d "{\"commit_sha\":\"$GITHUB_SHA\",\"ref\":\"$GITHUB_REF\",\"sarif\":\"$(base64 -w 0 $sarif_file)\",\"tool_name\":\"OWASP Dependency-Check (Additional)\"}" \
$UPLOAD_URL || echo "Failed to upload additional SARIF file: $sarif_file"
fi
COUNT=$((COUNT + 1))
done
continue-on-error: true
# Copy SARIF to artifact directory for persistent storage
- name: Upload SCA Results as Artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: sca-results
path: ./docs/reports/
sbom-generation:
name: Software Bill of Materials
runs-on: ubuntu-latest
needs: sca-scanning
steps:
- name: Checkout code
uses: actions/checkout@v4
# Create docs/reports directory if it doesn't exist
- name: Ensure docs/reports directory exists
run: mkdir -p ./docs/reports
- name: Generate SBOM with CycloneDX
uses: CycloneDX/gh-node-module-generatebom@master
with:
path: './xss'
output: './docs/reports/angular-xss-sbom.json'
- name: Upload SBOM as artifact
uses: actions/upload-artifact@v4
with:
name: angular-xss-sbom
path: './docs/reports/angular-xss-sbom.json'
dast-scanning:
name: Dynamic Application Security Testing
runs-on: ubuntu-latest
needs: sbom-generation
steps:
- name: Checkout code
uses: actions/checkout@v4
# Install necessary dependencies for ZAP scanning
- name: Install dependencies for ZAP
run: |
# Check if curl is installed
if ! command -v curl &> /dev/null; then
echo "Installing curl"
apt-get update && apt-get install -y curl
fi
# Install Docker Compose plugin if needed
- name: Set up Docker Compose
run: |
# Check if docker-compose is available
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
echo "Installing Docker Compose plugin"
# Install Docker Compose V2
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
fi
# Verify Docker and Docker Compose are installed
echo "Docker version:"
docker --version
echo "Docker Compose version:"
docker compose version || docker-compose --version || echo "Docker Compose not installed properly"
- name: Start Angular XSS application with Docker Compose
run: |
cd ./xss
# Try using docker compose command (v2) first, if it fails try docker-compose (v1)
echo "Starting containers with docker compose..."
docker compose build || docker-compose build || (echo "Docker Compose build failed" && exit 1)
docker compose up -d || docker-compose up -d || (echo "Docker Compose up failed" && exit 1)
# Wait for application to be ready
echo "Waiting for application to become available..."
sleep 60
# Create docs/reports directory if it doesn't exist
- name: Ensure docs/reports directory exists
run: mkdir -p ./docs/reports
# Create and verify ZAP rules file for scanning
- name: Set up ZAP rules file
run: |
# Check if zap-rules.tsv exists in the repo
echo "Checking for ZAP rules files..."
echo "Current directory: $(pwd)"
ls -la
if [ -f "zap-rules.tsv" ]; then
echo "ZAP rules file already exists in the root directory"
elif [ -f "./angular-xss/zap-rules.tsv" ]; then
echo "Found ZAP rules in angular-xss directory, copying to root"
cp ./angular-xss/zap-rules.tsv ./
else
echo "ZAP rules file not found, creating a basic one"
echo '10016 IGNORE http://localhost:4200 (IGNORE: A technology has been identified)' > zap-rules.tsv
echo '10020 IGNORE http://localhost:4200 (IGNORE: X-Frame-Options Header Not Set)' >> zap-rules.tsv
echo '10021 IGNORE http://localhost:4200 (IGNORE: X-Content-Type-Options Header Missing)' >> zap-rules.tsv
echo '10038 IGNORE http://localhost:4200 (IGNORE: Content Security Policy (CSP) Header Not Set)' >> zap-rules.tsv
echo '10049 IGNORE http://localhost:4200 (IGNORE: Non-Storable Content)' >> zap-rules.tsv
echo '40012 FAIL http://localhost:4200 (FAIL: Cross Site Scripting (Reflected))' >> zap-rules.tsv
fi
# Verify the rules file exists and show content
echo "Verifying ZAP rules file location and content:"
ls -la zap-rules.tsv
cat zap-rules.tsv
# Create a temporary directory with proper permissions for ZAP to write reports
- name: Create writable temp directory for ZAP
run: |
mkdir -p /tmp/zap-output
chmod 777 /tmp/zap-output
echo "Created writable directory for ZAP reports at /tmp/zap-output"
ls -la /tmp/zap-output
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.11.0
continue-on-error: true
with:
target: 'http://localhost:4200'
allow_issue_writing: true
# Use Docker's /tmp directory which should be writable
cmd_options: '-a -j -T 10 -w /tmp/zap-output/zap-baseline-report.md'
rules_file_name: 'zap-rules.tsv'
issue_title: 'ZAP Baseline Scan Report'
artifact_name: 'zap-baseline-report'
docker_name: 'ghcr.io/zaproxy/zaproxy:stable'
- name: ZAP Full Scan
uses: zaproxy/action-full-scan@v0.8.0
continue-on-error: true
with:
target: 'http://localhost:4200'
allow_issue_writing: true
# Use Docker's /tmp directory which should be writable
cmd_options: '-a -j -T 10 -w /tmp/zap-output/zap-full-scan-report.md'
rules_file_name: 'zap-rules.tsv'
issue_title: 'ZAP Full Scan Report'
docker_name: 'ghcr.io/zaproxy/zaproxy:stable'
# Copy ZAP reports to docs/reports directory
- name: Copy ZAP reports to docs/reports
run: |
echo "Looking for ZAP report files in various locations..."
# Check in /tmp/zap-output where we directed ZAP to write reports
echo "Checking in /tmp/zap-output:"
ls -la /tmp/zap-output || echo "Directory not found"
# Check in current directory
echo "Checking in current directory:"
find . -maxdepth 2 -name "*report*.md" -o -name "*report*.html" -o -name "*report*.json"
# Try to copy from our specific ZAP output directory first
if [ -d "/tmp/zap-output" ]; then
echo "Copying reports from /tmp/zap-output:"
cp -v /tmp/zap-output/*.* ./docs/reports/ 2>/dev/null || echo "No files to copy from /tmp/zap-output"
fi
# Then try multiple possible filenames and locations for the reports
for report in \
./zap-baseline-report.md \
./zap-full-scan-report.md \
./baseline-report.md \
./full-scan-report.md \
./report_md.md \
/tmp/zap-baseline-report.md \
/tmp/zap-full-scan-report.md \
/zap/wrk/zap-baseline-report.md \
/zap/wrk/zap-full-scan-report.md; do
if [ -f "$report" ]; then
echo "Found report: $report"
cp -v "$report" ./docs/reports/
fi
done
# If no reports were found, create placeholders
if [ ! -f "./docs/reports/zap-baseline-report.md" ] && [ ! -f "./docs/reports/baseline-report.md" ]; then
echo "Creating placeholder for ZAP baseline report"
echo "# ZAP Baseline Scan Report (Placeholder)
This is a placeholder for the ZAP baseline scan report.

Check failure on line 496 in .github/workflows/devsecops-pipeline.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/devsecops-pipeline.yml

Invalid workflow file

You have an error in your yaml syntax on line 496
The actual scan may have failed to generate a report file due to permissions issues.
" > ./docs/reports/zap-baseline-report.md
fi
if [ ! -f "./docs/reports/zap-full-scan-report.md" ] && [ ! -f "./docs/reports/full-scan-report.md" ]; then
echo "Creating placeholder for ZAP full scan report"
echo "# ZAP Full Scan Report (Placeholder)
This is a placeholder for the ZAP full scan report.
The actual scan may have failed to generate a report file due to permissions issues.
" > ./docs/reports/zap-full-scan-report.md
fi
# Check if any reports were copied or created
echo "Contents of docs/reports directory:"
ls -la ./docs/reports/
- name: Upload ZAP Report
uses: actions/upload-artifact@v4
if: always()
with:
name: zap-reports
path: ./docs/reports/
- name: Stop Docker Containers
if: always()
run: |
cd ./xss
# Try both docker compose (v2) and docker-compose (v1) commands
docker compose down || docker-compose down || true
# Verify containers are stopped
echo "Checking for running containers:"
docker ps
save-reports:
name: Save Scan Reports
runs-on: ubuntu-latest
needs: [secrets-scanning, sast-scanning, sca-scanning, sbom-generation, dast-scanning]
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ github.token }} # Use GitHub token for authentication
fetch-depth: 0 # Full history for proper commits
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
- name: Ensure reports directory exists
run: mkdir -p ./docs/reports
- name: Save all scan reports to docs/reports
run: |
# Create comprehensive report index file
echo "# DevSecOps Scan Reports" > ./docs/reports/README.md
echo "Generated on $(date)" >> ./docs/reports/README.md
echo "" >> ./docs/reports/README.md
echo "## Available Reports" >> ./docs/reports/README.md
# Process all artifacts and copy to docs/reports
for artifact_dir in ./artifacts/*; do
if [ -d "$artifact_dir" ]; then
artifact_name=$(basename "$artifact_dir")
echo "Processing artifact: $artifact_name"
# Copy all files from the artifact directory to docs/reports
cp -v $artifact_dir/* ./docs/reports/ 2>/dev/null || echo "No files to copy from $artifact_name"
# Add entry to the report index
echo "- **$artifact_name**" >> ./docs/reports/README.md
for file in $artifact_dir/*; do
if [ -f "$file" ]; then
filename=$(basename "$file")
echo " - [$filename](./$filename)" >> ./docs/reports/README.md
fi
done
fi
done
# Check if any reports were saved
echo "Saved reports in docs/reports directory:"
ls -la ./docs/reports/
# Commit and push the reports to the repository
- name: Commit and push reports to the repository
run: |
# Configure git
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
# Check for changes in remote and pull if needed
echo "Checking for updates from remote..."
git pull origin ${GITHUB_REF_NAME} --no-rebase || echo "Failed to pull, proceeding anyway"
# Ensure docs/reports directory exists
mkdir -p ./docs/reports/
# Stage the changes
echo "Adding report files to git..."
git add ./docs/reports/
# Verify changes were staged
git status
# Check if there are changes to commit
if git diff --staged --quiet; then
echo "No changes to commit"
else
# Commit the changes
echo "Committing report files..."
git commit -m "Add security scan reports [skip ci]"
# Push to the repository
echo "Pushing changes to the repository..."
git push origin ${GITHUB_REF_NAME}
echo "Reports have been committed and pushed to the repository."
echo "View them at: https://github.com/${GITHUB_REPOSITORY}/tree/${GITHUB_REF_NAME}/docs/reports"
fi
continue-on-error: true # Continue workflow even if push fails
defectdojo-import:
name: Import Results to DefectDojo
runs-on: ubuntu-latest
needs: [save-reports]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
# Copy artifacts to docs/reports for local usage
- name: Copy artifacts to docs/reports
run: |
mkdir -p ./docs/reports
echo "Copying all artifacts to docs/reports directory..."
# TruffleHog results
if [ -d "./artifacts/trufflehog-results" ]; then
echo "Found TruffleHog results"
cp ./artifacts/trufflehog-results/* ./docs/reports/ || echo "No TruffleHog files to copy"
else
echo "No TruffleHog results directory found"
fi
# CodeQL results
if [ -d "./artifacts/codeql-results" ]; then
echo "Found CodeQL results"
cp ./artifacts/codeql-results/* ./docs/reports/ || echo "No CodeQL files to copy"
else
echo "No CodeQL results directory found"
fi
# SCA results
if [ -d "./artifacts/sca-results" ]; then
echo "Found SCA results"
cp ./artifacts/sca-results/* ./docs/reports/ || echo "No SCA files to copy"
else
echo "No SCA results directory found"
fi
# SBOM results
if [ -d "./artifacts/angular-xss-sbom" ]; then
echo "Found SBOM results"
cp ./artifacts/angular-xss-sbom/* ./docs/reports/ || echo "No SBOM files to copy"
else
echo "No SBOM results directory found"
fi
# ZAP results
if [ -d "./artifacts/zap-reports" ]; then
echo "Found ZAP results"
cp ./artifacts/zap-reports/* ./docs/reports/ || echo "No ZAP files to copy"
else
echo "No ZAP results directory found"
fi
# Verify copied files
echo "Files in docs/reports directory after copying artifacts:"
ls -la ./docs/reports/
# Run verification script to check for required reports
echo "Running verification script..."
chmod +x ./verify-reports.sh
./verify-reports.sh