Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0acb5f4
Add Ash, Dependabot, Lint check workflows for Pull Requests
dheerajoruganty Aug 27, 2025
e0c8a04
Fix ASH workflow: Use pip install and direct ash command
dheerajoruganty Aug 27, 2025
ea63ccb
Fix ASH workflow: Install scanner dependencies manually
dheerajoruganty Aug 27, 2025
a855d76
Add ASH scanner validation test files
dheerajoruganty Aug 27, 2025
46a7948
Improve ASH workflow: remove emojis and install missing scanners
dheerajoruganty Aug 27, 2025
832bb49
Fix ASH workflow: update comments, remove redundant info, fail on errors
dheerajoruganty Aug 27, 2025
c4f840c
Fix workflow: ensure comments are posted even when ASH finds issues
dheerajoruganty Aug 27, 2025
2fdc4bf
Improve ASH report filtering with better regex
dheerajoruganty Aug 27, 2025
272e7ee
Fix ASH workflow to use terminal output for correct scanner status
dheerajoruganty Aug 27, 2025
dca86c5
Fix table extraction with more robust line number approach
dheerajoruganty Aug 27, 2025
fc7f572
Fix ASH output capture and table extraction
dheerajoruganty Aug 27, 2025
529ddd8
Fix detailed findings extraction and clean up debug output
dheerajoruganty Aug 27, 2025
e641bfd
Hybrid approach: terminal table + markdown detailed findings
dheerajoruganty Aug 27, 2025
acc1d09
Simplify ASH workflow processing
dheerajoruganty Aug 27, 2025
629e381
Switch to ASH container mode for reliable scanner execution
dheerajoruganty Aug 27, 2025
4979419
Fix container mode to scan only changed files
dheerajoruganty Aug 27, 2025
b987bdc
Add debugging to ASH workflow processing
dheerajoruganty Aug 27, 2025
968f231
Fix ANSI color codes breaking scanner table display
dheerajoruganty Aug 27, 2025
7029c10
Add verbose output and markdown table format
dheerajoruganty Aug 27, 2025
ed7cff6
Expand explanations with detailed descriptions and examples
dheerajoruganty Aug 27, 2025
8b625a7
Add timestamp to comment updates
dheerajoruganty Aug 27, 2025
22300bb
Ensure single comment updates with unique identifier
dheerajoruganty Aug 27, 2025
cce4655
Add debugging to identify duplicate comment issue
dheerajoruganty Aug 27, 2025
f502d6b
Fix duplicate comments by cleaning up and using most recent
dheerajoruganty Aug 27, 2025
67350fb
Update ASH security scan workflow with container mode and improved co…
dheerajoruganty Aug 27, 2025
9fcb400
Configure ASH container to show all security findings instead of limi…
dheerajoruganty Aug 27, 2025
69955d6
Configure ASH container to show all security findings instead of limi…
dheerajoruganty Aug 27, 2025
f93e6bc
Merge branch 'awslabs:main' into main
dheerajoruganty Sep 3, 2025
d8012ba
Modify workflows to comment on runs
dheerajoruganty Sep 3, 2025
2492c6c
Update workflows from main branch with ASH security scanning improvem…
dheerajoruganty Sep 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .ash.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/awslabs/automated-security-helper/refs/heads/main/automated_security_helper/schemas/AshConfig.json

project_name: amazon-bedrock-agentcore-samples

global_settings:
severity_threshold: MEDIUM
fail_on_findings: false # Don't fail CI, just report
ignore_paths: []

scanners:
bandit:
enabled: true
options:
confidence_level: MEDIUM
severity_level: medium

semgrep:
enabled: true
options:
rules: ['p/ci', 'p/security-audit']
timeout: 300

detect-secrets:
enabled: true
options:
exclude_lines: []

checkov:
enabled: true
options:
framework: ['all']

cfn-nag:
enabled: true

cdk-nag:
enabled: true
options:
nag_packs: ['AWS_SOLUTIONS']

npm-audit:
enabled: true
options:
audit_level: moderate

grype:
enabled: true
options:
severity: medium

syft:
enabled: true

reporters:
markdown:
enabled: true
options:
include_detailed_findings: true
max_detailed_findings: 1000 # Show all findings instead of default 20

json:
enabled: true
options:
pretty_print: true

sarif:
enabled: true
options:
include_help_uri: true
170 changes: 170 additions & 0 deletions .github/workflows/ash-full-repository-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
name: ASH Full Repository Scan

on:
push:
branches: [ main ]
schedule:
# Run at 2 AM UTC on the 1st of every month
- cron: '0 2 1 * *'
workflow_dispatch: # Allow manual triggering

permissions:
contents: read
security-events: write # For uploading SARIF results to GitHub Security tab

jobs:
full-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Install ASH
run: pip install git+https://github.com/awslabs/[email protected]

- name: Run ASH full repository scan
run: |
# Create ASH config for comprehensive scanning
cat > .ash_config.yaml << 'EOF'
reporters:
markdown:
enabled: true
options:
include_detailed_findings: true
max_detailed_findings: 1000
sarif:
enabled: true
EOF

# Run ASH on entire repository
ash --mode container --config .ash_config.yaml 2>&1 | tee ash-output.log
continue-on-error: true

- name: Generate scan summary
id: scan-summary
run: |
SUMMARY_FILE="ash-summary.md"

echo "# ASH Security Scan - Full Repository Report" > "$SUMMARY_FILE"
echo "" >> "$SUMMARY_FILE"
echo "**Scan Date:** $(date -u +%Y-%m-%dT%H:%M:%S+00:00)" >> "$SUMMARY_FILE"
echo "**Trigger:** ${{ github.event_name }}" >> "$SUMMARY_FILE"
if [ "${{ github.event_name }}" == "push" ]; then
echo "**Commit:** ${{ github.sha }}" >> "$SUMMARY_FILE"
echo "**Pushed by:** ${{ github.actor }}" >> "$SUMMARY_FILE"
elif [ "${{ github.event_name }}" == "schedule" ]; then
echo "**Type:** Monthly scheduled scan" >> "$SUMMARY_FILE"
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "**Type:** Manual trigger by ${{ github.actor }}" >> "$SUMMARY_FILE"
fi
echo "" >> "$SUMMARY_FILE"

# Extract and format scan results
if [ -f "ash-output.log" ]; then
# Find the table boundaries
TABLE_START=$(grep -n "ASH Scan Results Summary" ash-output.log | head -1 | cut -d: -f1 || echo "0")
TABLE_END=$(grep -n "source-dir:" ash-output.log | head -1 | cut -d: -f1 || echo "0")

if [ "$TABLE_START" != "0" ] && [ "$TABLE_END" != "0" ] && [ "$TABLE_END" -gt "$TABLE_START" ]; then
echo "## Scanner Results Summary" >> "$SUMMARY_FILE"
echo "" >> "$SUMMARY_FILE"

# Convert terminal table to markdown
echo "| Scanner | S | C | H | M | L | I | Time | Action | Result | Thresh |" >> "$SUMMARY_FILE"
echo "|---------|---|---|---|---|---|---|------|--------|--------|--------|" >> "$SUMMARY_FILE"
sed -n "${TABLE_START},${TABLE_END}p" ash-output.log | \
sed 's/\x1b\[[0-9;]*m//g' | \
grep "^│" | \
sed 's/│/|/g' | \
sed 's/^ *|/|/' | \
sed 's/| *$/|/' >> "$SUMMARY_FILE"
echo "" >> "$SUMMARY_FILE"
fi

# Check for findings
if grep -q "Actionable findings detected!" ash-output.log; then
echo "has_findings=true" >> $GITHUB_OUTPUT
echo "**Status:** ⚠️ Security findings detected" >> "$SUMMARY_FILE"
else
echo "has_findings=false" >> $GITHUB_OUTPUT
echo "**Status:** ✅ No security issues found" >> "$SUMMARY_FILE"
fi
fi

# Include detailed findings if available
if [ -f ".ash/ash_output/reports/ash.summary.md" ]; then
echo "" >> "$SUMMARY_FILE"
echo "## Detailed Findings" >> "$SUMMARY_FILE"
echo "" >> "$SUMMARY_FILE"
grep -A 1000 "Detailed Findings" ".ash/ash_output/reports/ash.summary.md" | \
grep -v -E '^(Time since scan:|Report generated:)' | \
grep -v 'Report generated by Automated Security Helper' >> "$SUMMARY_FILE" || true
fi

- name: Upload ASH results as artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: ash-full-scan-${{ github.run_id }}
path: |
.ash/
ash-output.log
ash-summary.md
retention-days: 90

- name: Upload SARIF results to GitHub Security
if: always() && hashFiles('.ash/ash_output/reports/ash.sarif') != ''
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: .ash/ash_output/reports/ash.sarif
category: ash-security-scan

- name: Create issue for critical findings (monthly scan only)
if: github.event_name == 'schedule' && steps.scan-summary.outputs.has_findings == 'true'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const summaryPath = 'ash-summary.md';

if (fs.existsSync(summaryPath)) {
const summaryContent = fs.readFileSync(summaryPath, 'utf8');

// Create issue for monthly scan findings
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `🔒 ASH Security Scan - Monthly Report (${new Date().toISOString().split('T')[0]})`,
body: summaryContent + '\n\n---\n*This issue was automatically created by the monthly security scan workflow.*',
labels: ['security', 'automated-scan']
});
}

- name: Job summary
if: always()
run: |
echo "## ASH Security Scan Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

if [ -f "ash-summary.md" ]; then
cat ash-summary.md >> $GITHUB_STEP_SUMMARY
else
echo "No scan summary available." >> $GITHUB_STEP_SUMMARY
fi

echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "*Full scan results are available in the [workflow artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})*" >> $GITHUB_STEP_SUMMARY

if [ "${{ steps.scan-summary.outputs.has_findings }}" == "true" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "⚠️ **Action Required:** Security findings were detected. Please review the results and address any critical issues." >> $GITHUB_STEP_SUMMARY
fi
131 changes: 131 additions & 0 deletions .github/workflows/ash-security-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: ASH Security Scan - Post Comments

on:
workflow_run:
workflows: ["ASH Security Scan"]
types:
- completed

permissions:
pull-requests: write
actions: read

jobs:
comment:
runs-on: ubuntu-latest
if: github.event.workflow_run.event == 'pull_request'
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: ash-security-results
path: /tmp/ash-results
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Get PR information
id: pr-info
run: |
if [ -f /tmp/ash-results/pr_number.txt ]; then
PR_NUMBER=$(cat /tmp/ash-results/pr_number.txt)
echo "pr_number=${PR_NUMBER}" >> $GITHUB_OUTPUT
echo "Found PR number: ${PR_NUMBER}"
else
echo "No PR number found in artifacts"
exit 1
fi

if [ -f /tmp/ash-results/pr_sha.txt ]; then
PR_SHA=$(cat /tmp/ash-results/pr_sha.txt)
echo "pr_sha=${PR_SHA}" >> $GITHUB_OUTPUT
echo "Found PR SHA: ${PR_SHA}"
fi

- name: Post comment on PR
if: steps.pr-info.outputs.pr_number
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const commentPath = '/tmp/ash-results/pr_comment.md';

if (!fs.existsSync(commentPath)) {
console.log('No comment file found in artifacts');
return;
}

const commentBody = fs.readFileSync(commentPath, 'utf8');
const prNumber = parseInt('${{ steps.pr-info.outputs.pr_number }}');
const prSha = '${{ steps.pr-info.outputs.pr_sha }}';

if (!prNumber) {
console.log('Invalid PR number');
return;
}

// Get existing comments
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
});

// Find ALL ASH security scan comments
const ashComments = comments.filter(comment =>
comment.user.type === 'Bot' &&
(comment.body.includes('<!-- ASH-SECURITY-SCAN-COMMENT -->') ||
comment.body.includes('## Security Scan Results') ||
comment.body.includes('Latest scan for commit:') ||
comment.body.includes('ASH Security Scan Report'))
);

console.log(`Found ${ashComments.length} ASH security scan comments`);

// Use the most recent ASH comment (highest ID = most recent)
const existingComment = ashComments.length > 0 ?
ashComments.sort((a, b) => b.id - a.id)[0] : null;

// Delete any duplicate/older ASH comments (keep only the most recent one)
if (ashComments.length > 1) {
console.log(`Cleaning up ${ashComments.length - 1} duplicate ASH comments`);
for (const comment of ashComments.slice(1)) {
try {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: comment.id,
});
console.log(`Deleted duplicate comment ${comment.id}`);
} catch (error) {
console.log(`Failed to delete comment ${comment.id}: ${error.message}`);
}
}
}

// Add commit and timestamp info to the body
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC';
const shortSha = prSha ? prSha.substring(0, 7) : 'unknown';
const enhancedBody = `**Latest scan for commit:** \`${shortSha}\` **| Updated:** ${timestamp}\n\n${commentBody}\n\n<!-- ASH-SECURITY-SCAN-COMMENT -->`;

if (existingComment) {
// Update existing comment
console.log(`Updating existing comment ${existingComment.id}`);
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body: enhancedBody
});
console.log('Successfully updated existing ASH security scan comment');
} else {
// Create new comment
console.log('No existing ASH comment found, creating new one');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: enhancedBody
});
console.log('Successfully created new ASH security scan comment');
}
Loading
Loading