Skip to content

Commit cc836f2

Browse files
Edgar PosadaEdgar Posada
authored andcommitted
Release v0.3.0: Config-driven fail gates, exclude paths, Docker detection, image scanning, inline PR comments
1 parent bdefeb1 commit cc836f2

File tree

11 files changed

+1727
-264
lines changed

11 files changed

+1727
-264
lines changed

.github/workflows/security.yml

Lines changed: 478 additions & 78 deletions
Large diffs are not rendered by default.

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Binaries
2+
devsecops
3+
devsecops-*
4+
5+
# IDE
6+
.vscode/
7+
.idea/
8+
9+
# OS
10+
.DS_Store
11+
12+
# Claude Code
13+
CLAUDE.md

CHANGELOG.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,96 @@ and this project adheres (loosely) to [Semantic Versioning](https://semver.org/s
1313

1414
---
1515

16+
## [0.3.0] - 2025-01-XX
17+
18+
### Added
19+
20+
- **Config-driven fail gates** 🎯:
21+
- New `fail_on` configuration in `security-config.yml`
22+
- Define per-tool thresholds that fail CI builds:
23+
- `gitleaks`: Fail on secret count threshold (default: 0)
24+
- `semgrep`: Fail on finding count threshold (default: 10)
25+
- `trivy_critical`, `trivy_high`, `trivy_medium`, `trivy_low`: Fail on vulnerability counts
26+
- Set threshold to `-1` to disable specific gate
27+
- Workflow now exits with error code 1 when thresholds exceeded
28+
- Summary status shows `PASS` or `FAIL` based on thresholds
29+
30+
- **Exclude paths support** 🚫:
31+
- New `exclude_paths` configuration to reduce scanning noise
32+
- Applies to all enabled scanners:
33+
- Semgrep: Uses `--exclude` flags
34+
- Gitleaks: Generates `.gitleaks.toml` with path allowlist
35+
- Trivy: Uses `skip-dirs` parameter
36+
- Common exclusions: `vendor/`, `node_modules/`, `test/`, etc.
37+
38+
- **Dockerfile detection** 🐳:
39+
- Automatic detection of Dockerfile and docker-compose.yml
40+
- Added `HasDocker` and `DockerImages` fields to `ProjectInfo`
41+
- `devsecops detect` now shows Docker status
42+
- Parses Dockerfile to extract base images
43+
44+
- **Trivy image scanning** 📦:
45+
- Automatic Docker image scanning when Dockerfile detected
46+
- Builds temporary image (`devsecops-scan-temp:latest`) for scanning
47+
- Generates `trivy-image.json` artifact
48+
- Image vulnerabilities included in summary and PR comments
49+
- Same fail gates apply to both FS and image scans
50+
51+
- **Inline "Fix-it" PR comments** 💬:
52+
- Detailed, file/line-specific security comments on PRs
53+
- Semgrep findings:
54+
- Shows severity, rule ID, and message
55+
- Includes fix suggestions when available
56+
- Links to security references
57+
- Gitleaks findings:
58+
- Highlights secret location
59+
- Provides remediation steps
60+
- Warns about credential rotation
61+
- Only comments on changed files in the PR
62+
- Limited to 10 Semgrep + 5 Gitleaks comments per run (prevents spam)
63+
64+
- **Enhanced PR summary comments**:
65+
- Now shows clear **PASS/FAIL status** based on fail gates
66+
- Displays blocking issue count
67+
- Separate sections for Trivy FS and Trivy Image results
68+
- Idempotent updates (no duplicate comments)
69+
70+
- **Structured summary.json v0.3.0**:
71+
- New fields:
72+
- `status`: "PASS" or "FAIL"
73+
- `blocking_count`: Number of issues exceeding thresholds
74+
- `trivy_image`: Image scan results (when Dockerfile present)
75+
- Ready for dashboard integrations and trend analysis
76+
77+
### Changed
78+
79+
- **Updated `security-config.yml` schema to v0.3.0**:
80+
- Added comprehensive `fail_on` configuration with defaults
81+
- Added `exclude_paths` with commented examples
82+
- Updated version to `"0.3.0"`
83+
84+
- **Workflow templates enhanced**:
85+
- Added Python step to extract config (requires PyYAML)
86+
- Config extraction happens early in workflow
87+
- Fail gate check runs at end (after artifacts upload)
88+
- Both Go and Node.js templates updated identically
89+
90+
- **README updated**:
91+
- Highlighted v0.3.0 features with 🆕 badges
92+
- Added fail gates and exclude paths examples
93+
- Updated configuration section with full v0.3.0 schema
94+
- Added customization instructions
95+
- Updated roadmap with release status
96+
97+
### Fixed
98+
99+
- **PyYAML installation** added to config extraction step (fixes `ModuleNotFoundError`)
100+
- **Dockerfile image extraction** now uses proper string parsing (not filepath.SplitList)
101+
- **Build stage detection** in Dockerfiles (skips `FROM ... AS stage` lines)
102+
- **Gitleaks JSON report generation** switched from `gitleaks-action` to direct CLI execution (enables fix-it comments to read findings)
103+
104+
---
105+
16106
## [0.2.0] - 2025-11-21
17107

18108
### Added

README.md

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,56 @@ DevSecOps Kit detects your project (Node.js or Go), generates a hardened GitHub
55

66
Designed for small teams, freelancers, and agencies who need practical DevSecOps without complexity.
77

8-
## 🚀 Key Features (v0.2.0)
8+
## 🚀 Key Features (v0.3.0)
99

1010
### 🔍 Automatic Project Detection
1111
Works out-of-the-box with:
1212

1313
- Node.js (`package.json`)
1414
- Go (`go.mod`)
15+
- **Docker** (Dockerfile detection) 🆕
1516

1617
### ⚙️ Auto-Generated Security Pipeline
1718
Generates a ready-to-run GitHub Actions workflow including:
1819

1920
- Semgrep (SAST)
2021
- Gitleaks (Secrets detection)
2122
- Trivy (FS + dependency scanning)
22-
- Hardenered permissions
23+
- **Trivy Image Scanning** (when Dockerfile present) 🆕
24+
- Hardened permissions
2325
- Artifact uploads
2426
- Timeout protections
2527

28+
### 🎯 Config-Driven Fail Gates 🆕
29+
Define thresholds that automatically fail CI builds:
30+
31+
```yaml
32+
fail_on:
33+
gitleaks: 0 # Fail if ANY secrets detected
34+
semgrep: 10 # Fail if 10+ findings
35+
trivy_critical: 0 # Fail if ANY critical vulnerabilities
36+
trivy_high: 5 # Fail if 5+ high severity vulnerabilities
37+
```
38+
39+
### 🚫 Exclude Paths 🆕
40+
Reduce noise by excluding directories from scans:
41+
42+
```yaml
43+
exclude_paths:
44+
- "vendor/"
45+
- "node_modules/"
46+
- "test/"
47+
- "*.test.js"
48+
```
49+
50+
### 💬 Inline "Fix-it" PR Comments 🆕
51+
Get detailed, actionable feedback directly on your code:
52+
53+
- File/line-specific comments for security issues
54+
- Remediation guidance for each finding
55+
- References to security best practices
56+
- Automatic comment placement on changed files only
57+
2658
### 🧙 Interactive Wizard
2759
```bash
2860
devsecops init --wizard
@@ -52,29 +84,39 @@ Each workflow produces:
5284
```
5385
artifacts/security/
5486
gitleaks-report.json
87+
semgrep-report.json
5588
trivy-fs.json
56-
summary.json
89+
trivy-image.json # When Dockerfile present
90+
summary.json # v0.3.0 schema
5791
```
5892

5993
The `summary.json` contains:
6094

6195
- Total secrets leaks
6296
- Vulnerability counts by severity
63-
- Ready for dashboards or fail-gates in future releases
97+
- **PASS/FAIL status based on thresholds** 🆕
98+
- **Blocking issue count** 🆕
99+
100+
### 💬 Enhanced PR Security Comments 🆕
101+
Every pull request receives:
64102

65-
### 💬 Automated PR Security Comment
66-
Every pull request receives a concise, updated comment summarizing:
103+
1. **Summary Comment** (updated, not duplicated):
104+
- Secrets found
105+
- FS & Image vulnerabilities
106+
- **Clear PASS/FAIL status**
107+
- **Blocking issue count**
67108

68-
- Secrets found
69-
- Vulnerabilities
70-
- PASS/FAIL recommendation
109+
2. **Inline Fix-it Comments**:
110+
- Specific file/line comments
111+
- Remediation guidance
112+
- Security references
71113

72-
### 📄 Expanded Configuration (v0.2.0)
114+
### 📄 Configuration (v0.3.0)
73115

74116
Generated automatically as:
75117

76118
```yaml
77-
version: "0.2.0"
119+
version: "0.3.0"
78120

79121
language: "golang"
80122
framework: ""
@@ -86,15 +128,32 @@ tools:
86128
trivy: true
87129
gitleaks: true
88130

89-
exclude_paths: []
90-
fail_on: {}
131+
# Exclude paths from scanning (reduces noise)
132+
exclude_paths:
133+
- "vendor/"
134+
- "node_modules/"
135+
- "test/"
136+
137+
# Fail gates - CI fails if thresholds exceeded
138+
fail_on:
139+
gitleaks: 0 # Fail if ANY secrets detected
140+
semgrep: 10 # Fail if 10+ Semgrep findings
141+
trivy_critical: 0 # Fail if ANY critical vulnerabilities
142+
trivy_high: 5 # Fail if 5+ high severity vulnerabilities
143+
trivy_medium: -1 # Disabled (set to number to enable)
144+
trivy_low: -1 # Disabled
91145

92146
notifications:
93147
pr_comment: true
94148
slack: false
95149
email: false
96150
```
97151
152+
**How to customize:**
153+
1. Run `devsecops init` to generate the config
154+
2. Edit `security-config.yml` to adjust thresholds and exclusions
155+
3. Commit changes - they take effect on next CI run
156+
98157
## 🛠️ Installation
99158

100159
### Option A — Install via Go
@@ -176,12 +235,12 @@ security-reports/
176235
177236
## 🧭 Roadmap
178237
179-
| Version | Features |
180-
|---------|----------|
181-
| **0.3.0** | Fail-on logic, exclude-paths integration, Semgrep JSON support |
182-
| **0.4.0** | Local CLI scans (`devsecops scan`) |
183-
| **0.5.0** | Expanded detection: Python, Java, Dockerfiles |
184-
| **1.0.0** | Full onboarding experience + multi-CI support |
238+
| Version | Features | Status |
239+
|---------|----------|--------|
240+
| **0.3.0** | Config-driven fail gates, exclude paths, Docker detection, image scanning, inline PR comments | ✅ **Released** |
241+
| **0.4.0** | Local CLI scans (`devsecops scan`), local report generation | 🚧 In Progress |
242+
| **0.5.0** | Python/Java detection, expanded framework support | 📋 Planned |
243+
| **1.0.0** | Full onboarding UX, multi-CI support (GitLab, Jenkins) | 📋 Planned |
185244
186245
## 🤝 Contributing
187246

cli/cmd/detect.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ var detectCmd = &cobra.Command{
3232
fmt.Printf(" RootDir: %s\n", info.RootDir)
3333
fmt.Printf(" Dependencies detected: %d\n", len(info.Dependencies))
3434

35+
if info.HasDocker {
36+
fmt.Println(" Docker: ✓ Detected")
37+
if len(info.DockerImages) > 0 {
38+
fmt.Println(" Docker Images:")
39+
for _, img := range info.DockerImages {
40+
fmt.Printf(" - %s\n", img)
41+
}
42+
}
43+
} else {
44+
fmt.Println(" Docker: Not detected")
45+
}
46+
3547
return nil
3648
},
3749
}

cli/detectors/detector.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"os"
77
"path/filepath"
8+
"strings"
89
)
910

1011
// ProjectInfo contains detected project information
@@ -14,6 +15,8 @@ type ProjectInfo struct {
1415
PackageFile string // e.g. "package.json", "go.mod"
1516
RootDir string // project root directory
1617
Dependencies []string // coarse list of deps (for future heuristics)
18+
HasDocker bool // true if Dockerfile detected
19+
DockerImages []string // list of Docker image names found
1720
}
1821

1922
// Detector interface for language/framework detection
@@ -51,9 +54,71 @@ func DetectProject(dir string) (*ProjectInfo, error) {
5154
return nil, errors.New("no supported project type detected")
5255
}
5356

57+
// Check for Docker (can coexist with any language)
58+
detectDocker(dir, bestMatch)
59+
5460
return bestMatch, nil
5561
}
5662

63+
// detectDocker checks for Dockerfile and docker-compose.yml, updates ProjectInfo in-place
64+
func detectDocker(dir string, info *ProjectInfo) {
65+
// Check for Dockerfile
66+
dockerfilePath := filepath.Join(dir, "Dockerfile")
67+
if fileExists(dockerfilePath) {
68+
info.HasDocker = true
69+
70+
// Try to extract image names from Dockerfile
71+
images := extractDockerImages(dockerfilePath)
72+
info.DockerImages = images
73+
}
74+
75+
// Also check for docker-compose.yml (indicates Docker usage)
76+
composePaths := []string{
77+
filepath.Join(dir, "docker-compose.yml"),
78+
filepath.Join(dir, "docker-compose.yaml"),
79+
}
80+
81+
for _, composePath := range composePaths {
82+
if fileExists(composePath) {
83+
info.HasDocker = true
84+
break
85+
}
86+
}
87+
}
88+
89+
// extractDockerImages parses Dockerfile to find image references
90+
// Returns a simple heuristic: finds lines like "FROM image:tag"
91+
func extractDockerImages(dockerfilePath string) []string {
92+
data, err := os.ReadFile(dockerfilePath)
93+
if err != nil {
94+
return nil
95+
}
96+
97+
var images []string
98+
content := string(data)
99+
lines := strings.Split(content, "\n")
100+
101+
for _, line := range lines {
102+
line = strings.TrimSpace(line)
103+
104+
// Look for "FROM <image>"
105+
if strings.HasPrefix(strings.ToUpper(line), "FROM ") {
106+
parts := strings.Fields(line) // Split by whitespace
107+
if len(parts) >= 2 {
108+
image := parts[1]
109+
// Skip build stages (FROM ... AS stage_name)
110+
// Check if next word is "AS"
111+
if len(parts) >= 3 && strings.ToUpper(parts[2]) == "AS" {
112+
continue // This is a build stage, skip it
113+
}
114+
images = append(images, image)
115+
}
116+
}
117+
}
118+
119+
return images
120+
}
121+
57122
// fileExists checks if a file exists
58123
func fileExists(path string) bool {
59124
_, err := os.Stat(path)

0 commit comments

Comments
 (0)