---
description: >
  HawkScan false positives guide: identifying false positives, deciding fix vs suppress, excludePaths and failureThreshold config, excluding scan plugins, reporting accepted risk.
globs:
alwaysApply: false
---
# False Positives and Accepted Risk

## Identifying False Positives

Not every finding from a DAST scan is a real vulnerability. Some common false positive
scenarios:

- **Health check or status endpoints** that intentionally return server info (e.g.,
  `/health`, `/actuator/info`) may trigger "Information Disclosure" findings
- **CORS headers** set intentionally permissive for public APIs
- **Deliberately open endpoints** (public API docs, login pages) flagged for missing
  authentication
- **Security headers on non-HTML responses** — CSP, X-Frame-Options findings on JSON
  API endpoints that never serve HTML
- **Rate limiting findings** on endpoints that are already behind an API gateway
  enforcing rate limits

## How to Decide: Fix or Suppress?

| Signal | Action |
|--------|--------|
| The finding describes real user-input handling with no sanitization | **Fix it** |
| The finding is on a test/mock endpoint not present in production | **Suppress** — exclude the path |
| The finding is on an intentionally open endpoint (health, docs) | **Suppress** — exclude the path |
| The finding is a header issue on a non-HTML API response | **Suppress** — exclude the path or accept the risk |
| You're unsure | **Fix it** — false negatives are worse than false positives |

## Suppression via Config

### Exclude specific paths from scanning

```yaml
app:
  excludePaths:
    - /health
    - /actuator/info
    - /swagger-ui
    - /api-docs
```

### Set failure threshold to ignore Low-severity findings

```yaml
hawk:
  failureThreshold: MEDIUM
```

This means the scan still reports Low findings but exits with code `0` instead of
`42` — they won't trigger the fix loop.

### Exclude specific scan plugins

If a specific check consistently produces false positives for your stack:

```yaml
hawk:
  scan:
    excludePlugins:
      - 10096  # Timestamp Disclosure (common false positive on API timestamps)
```

Use this sparingly. Prefer path exclusions over disabling entire plugins.

## Triaging via the API

Use `hawkop scan triage` to record false-positive decisions on the platform.
This is the preferred action over config suppression when the finding is a true
false positive — it creates an auditable, human-reviewable record.

### When to use API triage vs. config suppression

| Scenario | Action |
|----------|--------|
| Scanner is definitively wrong about this endpoint | **API triage** → `false-positive` with note |
| Path should permanently be excluded from scanning | **Config** → `excludePaths` in `stackhawk.yml` |
| Finding is noisy but not clearly wrong | **Fix it** — when in doubt, fix |
| Both apply (wrong AND should never scan) | Do both: triage existing finding + add to `excludePaths` |

### Single finding

```bash
hawkop scan triage \
  --scan <SCAN_UUID> \
  --hash <FINDING_HASH> \
  --status false-positive \
  --note "<reason — be specific: endpoint, finding name, why it's wrong>"
```

Example notes:
- `"CSP finding on JSON endpoint /api/health which never serves HTML; header inapplicable"`
- `"CORS wildcard on public read-only metrics API; no auth, no sensitive data"`
- `"Rate-limit finding on /api/events; rate limiting enforced at API gateway layer"`

### Bulk (multiple findings in one scan)

Write a `triage.yaml`:
```yaml
- finding_hash: "sha256hash1"
  status: FALSE_POSITIVE
  note: "Health endpoint — intentional server info exposure for monitoring"
- finding_hash: "sha256hash2"
  status: FALSE_POSITIVE
  note: "CORS permissive by design; public read-only API"
```

Then apply:
```bash
hawkop scan triage --scan <SCAN_UUID> --from-file triage.yaml
```

JSON format is also accepted (leading `[` is auto-detected). Max 100 actions per call — split into batches if needed.

### Agent rules for API triage

- ✅ Mark `FALSE_POSITIVE` autonomously — note must explain clearly why
- ✅ Use `ADD_COMMENT` to annotate without changing status
- ❌ **Never mark `RISK_ACCEPTED`** — human decision only
- ❌ **Never mark `ASSIGNED`** — human decision only
- ❌ Never suppress a finding by changing code to hide it from the scanner

```bash
# ADD_COMMENT example — for findings under review but not yet confirmed FP
hawkop scan triage \
  --scan <SCAN_UUID> \
  --hash <FINDING_HASH> \
  --status add-comment \
  --note "Reviewing with team; suspected false positive on /actuator/info"
```

### How to get the finding hash

The finding hash (`--hash`) comes from the scan JSON output:
```bash
hawk scan --json-output | jq '.findings[].hash'
```

Or from `hawkop scan get`:
```bash
hawkop scan get --app <APP_NAME> --detail full --format json | jq '.data.findings[].hash'
```

The scan UUID (`--scan`) comes from:
```bash
hawk scan --json-output | jq -r '.scan.id'
```

## Reporting Accepted Risk

When you encounter a finding that is a known false positive:

1. **Do not "fix" intentional behavior.** Changing a deliberately open health endpoint
   to require auth will break monitoring.
2. **Triage via the API** using `hawkop scan triage` (see section above) so the decision
   is recorded and auditable.
3. **Optionally add the path to `excludePaths`** in `stackhawk.yml` to prevent it from
   appearing on future scans — useful for paths that should never be scanned.
4. **Report it clearly:** "Finding X on path Y is a false positive because [reason].
   Marked via API triage with note: [note]."

## When in Doubt

If you cannot determine whether a finding is a false positive:
- **Fix it.** A false negative (missing a real vulnerability) is far worse than
  spending time fixing a false positive.
- Flag it in your report: "Fixed [finding] — if this was intentional behavior,
  the change can be reverted and the path added to excludePaths."
