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
51 changes: 51 additions & 0 deletions docs/security/suppressions/gosec.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,56 @@ The provenance file (JSON metadata about a download) is written with 0644 permis

---

## SDR-006: Contained Extraction Paths (G703)

**Rule:** G703 - Path traversal via taint analysis
**Severity:** High
**Instances:** 3
**Decision:** Suppress with `#nosec G703 -- SDR-006`

### Context

G703 flags `os.MkdirAll` and `os.OpenFile` sinks in `extractZip` whose path
argument is tainted by archive entry names, warning of zip-slip path traversal.

### Why Suppression is Appropriate

Every flagged sink operates only on `destPathClean`, which cannot escape the
extraction directory:

1. **Entry names are normalized and pre-filtered**: `filepath.FromSlash` +
`filepath.Clean`; `.` skipped; `..`, absolute paths, and volume names are
hard errors before any path is built.

2. **Joined path must stay under the prefix**: after `filepath.Join` +
`filepath.Clean`, a path that is neither the extraction dir itself nor
prefixed by it is a hard error. The analyzer cannot follow this
string-prefix containment proof, so it reports the residual taint at high
confidence despite the runtime gate.

3. **No symlink or special-file escape**: symlinks and non-regular files are
refused before any directory creation or file write.

### Proof

Covered by maintained regression tests, not by inspection alone:

- `TestExtractZipRejectsZipSlip` (`../evil` rejected; asserts nothing is
written outside the extraction dir)
- `TestExtractZipRejectsAbsolutePaths`
- `TestExtractZipRejectsSymlinks`
- `TestExtractZipNestedPaths` (legitimate nested paths still extract)

### Affected Locations

| File | Function | Purpose |
|------|----------|---------|
| main.go | extractZip | MkdirAll for entry dir (contained path) |
| main.go | extractZip | MkdirAll for entry parent dir (contained path) |
| main.go | extractZip | OpenFile for entry content (contained path) |

---

## Suppression Audit Log

| Date | SDR | Action | Author |
Expand All @@ -177,3 +227,4 @@ The provenance file (JSON metadata about a download) is written with 0644 permis
| 2026-01-10 | SDR-003 | Created | Claude Opus 4.5 |
| 2026-01-10 | SDR-004 | Created | Claude Opus 4.5 |
| 2026-01-10 | SDR-005 | Created | Claude Opus 4.5 |
| 2026-09-16 | SDR-006 | Created | Muse Spark |
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3435,14 +3435,14 @@ func extractZip(zipPath, extractDir string) error {
}

if f.FileInfo().IsDir() {
// #nosec G301 -- SDR-002: tar extraction dir
// #nosec G301,G703 -- SDR-002: tar extraction dir; SDR-006: prefix-contained path
if err := os.MkdirAll(destPathClean, 0o755); err != nil {
return fmt.Errorf("mkdir %s: %w", destPathClean, err)
}
continue
}

// #nosec G301 -- SDR-002: tar extraction dir
// #nosec G301,G703 -- SDR-002: tar extraction dir; SDR-006: prefix-contained path
if err := os.MkdirAll(filepath.Dir(destPathClean), 0o755); err != nil {
return fmt.Errorf("mkdir %s: %w", filepath.Dir(destPathClean), err)
}
Expand All @@ -3452,7 +3452,7 @@ func extractZip(zipPath, extractDir string) error {
return fmt.Errorf("open %s in zip: %w", f.Name, err)
}

// #nosec G302 -- SDR-003: extracted file permissions
// #nosec G302,G703 -- SDR-003: extracted file permissions; SDR-006: prefix-contained path
out, err := os.OpenFile(destPathClean, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
if err != nil {
_ = rc.Close()
Expand Down