Security Diff Workflows
This guide provides practical workflows for using jmo diff in common development scenarios.
Table of Contents
- Use Case 1: PR Review Comments
- Use Case 2: CI/CD Security Gate
- Use Case 3: Sprint Tracking
- Use Case 4: Release Validation
- Use Case 5: Historical Trend Analysis
- Advanced Workflows
- Troubleshooting
Use Case 1: PR Review Comments
Goal: Automatically comment on pull requests with security diff reports, showing only NEW issues introduced by the PR.
Workflow
graph LR
A[PR Opened] --> B[Scan Main Branch]
B --> C[Scan PR Branch]
C --> D[Generate Diff]
D --> E[Post PR Comment]
E --> F[Upload SARIF]
Implementation
GitHub Actions: See github-actions-diff.yml
GitLab CI: See gitlab-ci-diff.yml
Benefits
- ✅ Immediate feedback: Developers see new findings without leaving GitHub/GitLab
- ✅ Reduced noise: Only shows changes, not all findings
- ✅ Historical context: SARIF upload tracks findings over time
- ✅ Actionable: Developers can fix issues before merge
Example Output
# 🔍 Security Diff Report
**Baseline:** `main` (2025-11-05, balanced profile)
**Current:** `feature/new-api` (2025-11-05, balanced profile)
---
## 📊 Summary
| Metric | Count | Change |
|--------|-------|--------|
| **New Findings** | 3 | 🔴 +3 |
| **Resolved Findings** | 1 | ✅ -1 |
| **Modified Findings** | 0 | ➖ 0 |
| **Net Change** | +2 | 🔴 Worsening |
### New Findings by Severity
- 🔴 **HIGH**: 1
- 🟡 **MEDIUM**: 2
---
## ⚠️ New Findings (3)
### 🔴 HIGH (1)
<details>
<summary><b>SQL Injection in user query handler</b></summary>
**Rule:** `semgrep.sql-injection`
**File:** `src/api/users.py:127`
**Tool:** semgrep v1.50.0
**Message:**
Unsanitized user input flows into SQL query. Use parameterized queries.
**Remediation:**
```python
# BAD
query = f"SELECT * FROM users WHERE id = {user_id}"
# GOOD
query = "SELECT * FROM users WHERE id = ?"
cursor.execute(query, (user_id,))
---
## Use Case 2: CI/CD Security Gate
**Goal:** Block pull request merges if new CRITICAL or HIGH findings are introduced.
### Workflow
```bash
# Generate diff report
jmo diff baseline-results/ current-results/ --format json --output diff.json
# Extract new CRITICAL/HIGH count
NEW_COUNT=$(jq '.statistics.new_by_severity.CRITICAL + .statistics.new_by_severity.HIGH' diff.json)
# Fail if any found
if [ "$NEW_COUNT" -gt 0 ]; then
echo "❌ Security gate failed: $NEW_COUNT new CRITICAL/HIGH findings"
exit 1
fi
Configuration Options
Strict mode: Block on CRITICAL only
NEW_CRITICAL=$(jq '.statistics.new_by_severity.CRITICAL // 0' diff.json)
if [ "$NEW_CRITICAL" -gt 0 ]; then
exit 1
fi
Moderate mode: Block on CRITICAL/HIGH
NEW_COUNT=$(jq '(.statistics.new_by_severity.CRITICAL // 0) + (.statistics.new_by_severity.HIGH // 0)' diff.json)
if [ "$NEW_COUNT" -gt 0 ]; then
exit 1
fi
Relaxed mode: Block only if net change is negative
NET_CHANGE=$(jq '.statistics.net_change' diff.json)
if [ "$NET_CHANGE" -gt 5 ]; then # More than 5 new findings overall
exit 1
fi
Benefits
- ✅ Enforce standards: No high-risk code reaches main
- ✅ Clear feedback: Developers know exactly what to fix
- ✅ Customizable: Adjust thresholds per team/project
- ✅ Non-blocking option: Run informational mode with
allow_failure: true
Use Case 3: Sprint Tracking
Goal: Track security improvements over a sprint to measure remediation progress.
Workflow
At Sprint Start:
At Sprint End:
# Scan current state
jmo scan --repo . --profile balanced --results-dir sprint-end/
# Generate diff
jmo diff sprint-start/ sprint-end/ \
--format html \
--output sprint-report.html
# Generate JSON for metrics
jmo diff sprint-start/ sprint-end/ \
--format json \
--output sprint-metrics.json
Metrics to Track
# Extract sprint metrics
RESOLVED=$(jq '.statistics.total_resolved' sprint-metrics.json)
NEW=$(jq '.statistics.total_new' sprint-metrics.json)
NET=$(jq '.statistics.net_change' sprint-metrics.json)
TREND=$(jq -r '.statistics.trend' sprint-metrics.json)
echo "📊 Sprint Security Metrics:"
echo " - Issues resolved: $RESOLVED"
echo " - New issues: $NEW"
echo " - Net improvement: $NET"
echo " - Trend: $TREND"
Benefits
- ✅ Visualize progress: See security improvements over time
- ✅ Celebrate wins: Highlight resolved findings
- ✅ Identify trends: Detect if security is improving or degrading
- ✅ Team motivation: Gamify security remediation
Use Case 4: Release Validation
Goal: Ensure releases have fewer security issues than previous versions.
Workflow
# Scan previous release (from tag)
git checkout v1.0.0
jmo scan --repo . --profile deep --results-dir v1.0.0-results/
# Scan release candidate
git checkout v1.1.0-rc1
jmo scan --repo . --profile deep --results-dir v1.1.0-results/
# Generate diff
jmo diff v1.0.0-results/ v1.1.0-results/ \
--format md \
--output release-diff.md
# Gate: Ensure no new CRITICAL/HIGH
NEW_HIGH=$(jq '(.statistics.new_by_severity.CRITICAL // 0) + (.statistics.new_by_severity.HIGH // 0)' release-diff.json)
if [ "$NEW_HIGH" -gt 0 ]; then
echo "❌ Release blocked: $NEW_HIGH new CRITICAL/HIGH findings"
exit 1
fi
Pre-Release Checklist
- [ ] No new CRITICAL findings
- [ ] No new HIGH findings (or document exceptions)
- [ ] Net change is negative (more resolved than new)
- [ ] Trend is "improving" or "stable"
- [ ] All SARIF uploaded to Code Scanning
- [ ] Release notes include security improvements
Benefits
- ✅ Quality gate: Prevent security regressions in releases
- ✅ Audit trail: Document security posture per release
- ✅ Customer confidence: Show security improvements in release notes
Use Case 5: Historical Trend Analysis
Goal: Analyze security posture changes over multiple scans using SQLite storage.
Workflow
# Store scans in SQLite (automatic after every scan)
jmo scan --repo . --profile balanced --results-dir results/
# Scan auto-stored to ~/.jmo/scans.db
# List historical scans
jmo history list --last 10
# Compare two historical scans
jmo diff --scan abc123 --scan def456 --format json
# Generate trend analysis (requires SQLite storage)
jmo trends --last 10 --format html --output trends.html
Long-Term Metrics
# Query historical data
sqlite3 ~/.jmo/scans.db <<EOF
SELECT
timestamp_iso,
total_findings,
severity_counts
FROM scans
ORDER BY timestamp DESC
LIMIT 10;
EOF
Benefits
- ✅ Historical context: See security posture evolution
- ✅ Trend detection: Identify long-term improvements/regressions
- ✅ Executive reporting: Generate monthly/quarterly reports
- ✅ Compliance: Maintain audit trail of security scans
Advanced Workflows
Multi-Repository Comparison
Compare security posture across multiple repositories:
#!/bin/bash
# scan-all-repos.sh
REPOS=("frontend" "backend" "mobile-app")
for repo in "${REPOS[@]}"; do
echo "📊 Scanning $repo..."
cd "$repo"
jmo scan --repo . --profile fast --results-dir "../scans/$repo"
cd ..
done
# Generate cross-repo comparison
# (Custom script to aggregate findings)
Dependency Update Validation
Validate security impact of dependency updates:
# Before update
jmo scan --repo . --profile balanced --results-dir pre-update/
# Update dependencies
npm update
# or: pip install --upgrade -r requirements.txt
# After update
jmo scan --repo . --profile balanced --results-dir post-update/
# Check impact
jmo diff pre-update/ post-update/ \
--format md \
--output dependency-update-impact.md
Security Regression Testing
Integrate into automated test suites:
# pytest fixture for security diff
import pytest
import subprocess
import json
@pytest.fixture(scope="session")
def security_baseline():
"""Run baseline security scan once per test session."""
subprocess.run([
"jmo", "scan",
"--repo", ".",
"--profile", "fast",
"--results-dir", "test-baseline/"
], check=True)
def test_no_new_critical_findings(security_baseline):
"""Ensure no new CRITICAL findings introduced."""
# Scan current code
subprocess.run([
"jmo", "scan",
"--repo", ".",
"--profile", "fast",
"--results-dir", "test-current/"
], check=True)
# Generate diff
subprocess.run([
"jmo", "diff",
"test-baseline/", "test-current/",
"--format", "json",
"--output", "test-diff.json"
], check=True)
# Load diff
with open("test-diff.json") as f:
diff = json.load(f)
# Assert no new CRITICAL
new_critical = diff["statistics"]["new_by_severity"].get("CRITICAL", 0)
assert new_critical == 0, f"Found {new_critical} new CRITICAL findings"
Troubleshooting
Issue: Diff shows all findings as "new"
Cause: Fingerprint IDs changed between scans (e.g., different tool versions)
Solution:
# Ensure same tool versions
jmo scan --repo . --profile fast --tools trivy,semgrep --results-dir baseline/
jmo scan --repo . --profile fast --tools trivy,semgrep --results-dir current/
# Verify fingerprint stability
jq '.id' baseline/summaries/findings.json | head -5
jq '.id' current/summaries/findings.json | head -5
Issue: PR comment too large (>65k characters)
Cause: Too many findings in diff report
Solution:
# Filter to CRITICAL/HIGH only
jmo diff baseline/ current/ \
--format md \
--output pr-diff.md \
--severity CRITICAL,HIGH
# Or show only new findings
jmo diff baseline/ current/ \
--format md \
--output pr-diff.md \
--only new
Issue: Modification detection shows false positives
Cause: Tool vendors updated severity ratings or CWE mappings
Solution:
# Disable modification detection
jmo diff baseline/ current/ \
--no-modifications \
--format md \
--output pr-diff.md
# Or filter specific modification types
jmo diff baseline/ current/ \
--modification-types severity,priority \
--format md \
--output pr-diff.md
Issue: SARIF upload fails in GitHub Actions
Cause: Missing security-events: write permission
Solution:
Issue: Diff performance is slow (>5s)
Cause: Large result directories with many findings
Solution:
# Use --no-modifications for faster diffs
jmo diff baseline/ current/ \
--no-modifications \
--format json \
--output diff.json
# Or use SQLite mode (faster for repeated queries)
jmo diff --scan abc123 --scan def456 --format json
Best Practices
- Use consistent profiles: Always compare scans with the same profile (fast/balanced/deep)
- Version control baselines: Commit baseline results for reproducible comparisons
- Automate everything: Use CI/CD for consistent, automated diffing
- Filter intelligently: Use
--severityand--toolfilters to reduce noise - Document exceptions: If blocking on findings, document why specific findings are acceptable
- Monitor trends: Track security posture over time, not just point-in-time
- Educate developers: Share diff reports and celebrate security improvements
- Iterate on thresholds: Start strict, relax based on team feedback
Additional Resources
Last Updated: February 2026