Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
556 changes: 478 additions & 78 deletions .github/workflows/security.yml

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Binaries
devsecops
devsecops-*

# IDE
.vscode/
.idea/

# OS
.DS_Store

# Claude Code
CLAUDE.md
90 changes: 90 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,96 @@ and this project adheres (loosely) to [Semantic Versioning](https://semver.org/s

---

## [0.3.0] - 2025-01-XX

### Added

- **Config-driven fail gates** 🎯:
- New `fail_on` configuration in `security-config.yml`
- Define per-tool thresholds that fail CI builds:
- `gitleaks`: Fail on secret count threshold (default: 0)
- `semgrep`: Fail on finding count threshold (default: 10)
- `trivy_critical`, `trivy_high`, `trivy_medium`, `trivy_low`: Fail on vulnerability counts
- Set threshold to `-1` to disable specific gate
- Workflow now exits with error code 1 when thresholds exceeded
- Summary status shows `PASS` or `FAIL` based on thresholds

- **Exclude paths support** 🚫:
- New `exclude_paths` configuration to reduce scanning noise
- Applies to all enabled scanners:
- Semgrep: Uses `--exclude` flags
- Gitleaks: Generates `.gitleaks.toml` with path allowlist
- Trivy: Uses `skip-dirs` parameter
- Common exclusions: `vendor/`, `node_modules/`, `test/`, etc.

- **Dockerfile detection** 🐳:
- Automatic detection of Dockerfile and docker-compose.yml
- Added `HasDocker` and `DockerImages` fields to `ProjectInfo`
- `devsecops detect` now shows Docker status
- Parses Dockerfile to extract base images

- **Trivy image scanning** 📦:
- Automatic Docker image scanning when Dockerfile detected
- Builds temporary image (`devsecops-scan-temp:latest`) for scanning
- Generates `trivy-image.json` artifact
- Image vulnerabilities included in summary and PR comments
- Same fail gates apply to both FS and image scans

- **Inline "Fix-it" PR comments** 💬:
- Detailed, file/line-specific security comments on PRs
- Semgrep findings:
- Shows severity, rule ID, and message
- Includes fix suggestions when available
- Links to security references
- Gitleaks findings:
- Highlights secret location
- Provides remediation steps
- Warns about credential rotation
- Only comments on changed files in the PR
- Limited to 10 Semgrep + 5 Gitleaks comments per run (prevents spam)

- **Enhanced PR summary comments**:
- Now shows clear **PASS/FAIL status** based on fail gates
- Displays blocking issue count
- Separate sections for Trivy FS and Trivy Image results
- Idempotent updates (no duplicate comments)

- **Structured summary.json v0.3.0**:
- New fields:
- `status`: "PASS" or "FAIL"
- `blocking_count`: Number of issues exceeding thresholds
- `trivy_image`: Image scan results (when Dockerfile present)
- Ready for dashboard integrations and trend analysis

### Changed

- **Updated `security-config.yml` schema to v0.3.0**:
- Added comprehensive `fail_on` configuration with defaults
- Added `exclude_paths` with commented examples
- Updated version to `"0.3.0"`

- **Workflow templates enhanced**:
- Added Python step to extract config (requires PyYAML)
- Config extraction happens early in workflow
- Fail gate check runs at end (after artifacts upload)
- Both Go and Node.js templates updated identically

- **README updated**:
- Highlighted v0.3.0 features with 🆕 badges
- Added fail gates and exclude paths examples
- Updated configuration section with full v0.3.0 schema
- Added customization instructions
- Updated roadmap with release status

### Fixed

- **PyYAML installation** added to config extraction step (fixes `ModuleNotFoundError`)
- **Dockerfile image extraction** now uses proper string parsing (not filepath.SplitList)
- **Build stage detection** in Dockerfiles (skips `FROM ... AS stage` lines)
- **Gitleaks JSON report generation** switched from `gitleaks-action` to direct CLI execution (enables fix-it comments to read findings)

---

## [0.2.0] - 2025-11-21

### Added
Expand Down
97 changes: 78 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,56 @@ DevSecOps Kit detects your project (Node.js or Go), generates a hardened GitHub

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

## 🚀 Key Features (v0.2.0)
## 🚀 Key Features (v0.3.0)

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

- Node.js (`package.json`)
- Go (`go.mod`)
- **Docker** (Dockerfile detection) 🆕

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

- Semgrep (SAST)
- Gitleaks (Secrets detection)
- Trivy (FS + dependency scanning)
- Hardenered permissions
- **Trivy Image Scanning** (when Dockerfile present) 🆕
- Hardened permissions
- Artifact uploads
- Timeout protections

### 🎯 Config-Driven Fail Gates 🆕
Define thresholds that automatically fail CI builds:

```yaml
fail_on:
gitleaks: 0 # Fail if ANY secrets detected
semgrep: 10 # Fail if 10+ findings
trivy_critical: 0 # Fail if ANY critical vulnerabilities
trivy_high: 5 # Fail if 5+ high severity vulnerabilities
```

### 🚫 Exclude Paths 🆕
Reduce noise by excluding directories from scans:

```yaml
exclude_paths:
- "vendor/"
- "node_modules/"
- "test/"
- "*.test.js"
```

### 💬 Inline "Fix-it" PR Comments 🆕
Get detailed, actionable feedback directly on your code:

- File/line-specific comments for security issues
- Remediation guidance for each finding
- References to security best practices
- Automatic comment placement on changed files only

### 🧙 Interactive Wizard
```bash
devsecops init --wizard
Expand Down Expand Up @@ -52,29 +84,39 @@ Each workflow produces:
```
artifacts/security/
gitleaks-report.json
semgrep-report.json
trivy-fs.json
summary.json
trivy-image.json # When Dockerfile present
summary.json # v0.3.0 schema
```

The `summary.json` contains:

- Total secrets leaks
- Vulnerability counts by severity
- Ready for dashboards or fail-gates in future releases
- **PASS/FAIL status based on thresholds** 🆕
- **Blocking issue count** 🆕

### 💬 Enhanced PR Security Comments 🆕
Every pull request receives:

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

- Secrets found
- Vulnerabilities
- PASS/FAIL recommendation
2. **Inline Fix-it Comments**:
- Specific file/line comments
- Remediation guidance
- Security references

### 📄 Expanded Configuration (v0.2.0)
### 📄 Configuration (v0.3.0)

Generated automatically as:

```yaml
version: "0.2.0"
version: "0.3.0"

language: "golang"
framework: ""
Expand All @@ -86,15 +128,32 @@ tools:
trivy: true
gitleaks: true

exclude_paths: []
fail_on: {}
# Exclude paths from scanning (reduces noise)
exclude_paths:
- "vendor/"
- "node_modules/"
- "test/"

# Fail gates - CI fails if thresholds exceeded
fail_on:
gitleaks: 0 # Fail if ANY secrets detected
semgrep: 10 # Fail if 10+ Semgrep findings
trivy_critical: 0 # Fail if ANY critical vulnerabilities
trivy_high: 5 # Fail if 5+ high severity vulnerabilities
trivy_medium: -1 # Disabled (set to number to enable)
trivy_low: -1 # Disabled

notifications:
pr_comment: true
slack: false
email: false
```

**How to customize:**
1. Run `devsecops init` to generate the config
2. Edit `security-config.yml` to adjust thresholds and exclusions
3. Commit changes - they take effect on next CI run

## 🛠️ Installation

### Option A — Install via Go
Expand Down Expand Up @@ -176,12 +235,12 @@ security-reports/

## 🧭 Roadmap

| Version | Features |
|---------|----------|
| **0.3.0** | Fail-on logic, exclude-paths integration, Semgrep JSON support |
| **0.4.0** | Local CLI scans (`devsecops scan`) |
| **0.5.0** | Expanded detection: Python, Java, Dockerfiles |
| **1.0.0** | Full onboarding experience + multi-CI support |
| Version | Features | Status |
|---------|----------|--------|
| **0.3.0** | Config-driven fail gates, exclude paths, Docker detection, image scanning, inline PR comments | ✅ **Released** |
| **0.4.0** | Local CLI scans (`devsecops scan`), local report generation | 🚧 In Progress |
| **0.5.0** | Python/Java detection, expanded framework support | 📋 Planned |
| **1.0.0** | Full onboarding UX, multi-CI support (GitLab, Jenkins) | 📋 Planned |

## 🤝 Contributing

Expand Down
12 changes: 12 additions & 0 deletions cli/cmd/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ var detectCmd = &cobra.Command{
fmt.Printf(" RootDir: %s\n", info.RootDir)
fmt.Printf(" Dependencies detected: %d\n", len(info.Dependencies))

if info.HasDocker {
fmt.Println(" Docker: ✓ Detected")
if len(info.DockerImages) > 0 {
fmt.Println(" Docker Images:")
for _, img := range info.DockerImages {
fmt.Printf(" - %s\n", img)
}
}
} else {
fmt.Println(" Docker: Not detected")
}

return nil
},
}
Expand Down
65 changes: 65 additions & 0 deletions cli/detectors/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"os"
"path/filepath"
"strings"
)

// ProjectInfo contains detected project information

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 Secret Detected

Rule: generic-api-key
Match: sk-1234567890abcdefg...

⚠️ Action Required: Remove this secret immediately and:

  1. Rotate the compromised credential
  2. Use environment variables or secret management
  3. Never commit secrets to version control

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 Secret Detected

Rule: generic-api-key
Match: sk-1234567890abcdefg...

⚠️ Action Required: Remove this secret immediately and:

  1. Rotate the compromised credential
  2. Use environment variables or secret management
  3. Never commit secrets to version control

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 Secret Detected

Rule: generic-api-key
Match: sk-1234567890abcdefg...

⚠️ Action Required: Remove this secret immediately and:

  1. Rotate the compromised credential
  2. Use environment variables or secret management
  3. Never commit secrets to version control

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

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

// Check for Docker (can coexist with any language)
detectDocker(dir, bestMatch)

return bestMatch, nil
}

// detectDocker checks for Dockerfile and docker-compose.yml, updates ProjectInfo in-place
func detectDocker(dir string, info *ProjectInfo) {
// Check for Dockerfile
dockerfilePath := filepath.Join(dir, "Dockerfile")
if fileExists(dockerfilePath) {
info.HasDocker = true

// Try to extract image names from Dockerfile
images := extractDockerImages(dockerfilePath)
info.DockerImages = images
}

// Also check for docker-compose.yml (indicates Docker usage)
composePaths := []string{
filepath.Join(dir, "docker-compose.yml"),
filepath.Join(dir, "docker-compose.yaml"),
}

for _, composePath := range composePaths {
if fileExists(composePath) {
info.HasDocker = true
break
}
}
}

// extractDockerImages parses Dockerfile to find image references
// Returns a simple heuristic: finds lines like "FROM image:tag"
func extractDockerImages(dockerfilePath string) []string {
data, err := os.ReadFile(dockerfilePath)
if err != nil {
return nil
}

var images []string
content := string(data)
lines := strings.Split(content, "\n")

for _, line := range lines {
line = strings.TrimSpace(line)

// Look for "FROM <image>"
if strings.HasPrefix(strings.ToUpper(line), "FROM ") {
parts := strings.Fields(line) // Split by whitespace
if len(parts) >= 2 {
image := parts[1]
// Skip build stages (FROM ... AS stage_name)
// Check if next word is "AS"
if len(parts) >= 3 && strings.ToUpper(parts[2]) == "AS" {
continue // This is a build stage, skip it
}
images = append(images, image)
}
}
}

return images
}

// fileExists checks if a file exists
func fileExists(path string) bool {
_, err := os.Stat(path)
Expand Down
Loading
Loading