Skip to content
Open
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
57 changes: 57 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,63 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Fixed
- **Behavioral:** TruffleHog secret scanning no longer passes `--no-verification`
when `trufflehog_show_unverified` is off. That flag disabled verification
entirely, so every finding came back with `Verified: false` and was reported as
low severity — meaning no secret ever blocked a run on the setting's default
path, the exact inverse of the intended behavior. Verification now always runs,
and the setting controls only which result types are returned:
`--results=verified` when off, `--results=verified,unverified,unknown` when on.
Verified findings are critical and blocking; unverified findings remain low and
non-blocking.

**Three consequences on upgrade — read before bumping:**

1. **Runs will start failing that previously passed.** With
`trufflehog_show_unverified` off, verified secrets are now reported as
critical and block. Previously they were downgraded to low and ignored. This
is the intended behavior, but it lands as newly-red pipelines on the first
run after upgrading. It is not a new detection — those secrets were always
there, they were just never surfaced as blocking.
2. **TruffleHog now makes outbound network requests.** Verification is a live
check against third-party credential-validation endpoints (AWS, GitHub,
Slack, etc.) for every candidate secret. Runs that previously scanned fully
offline no longer do.
3. **Air-gapped and proxied environments will under-report.** When verification
cannot reach a validation endpoint, TruffleHog classifies the result as
`unknown` rather than verified or unverified. With
`trufflehog_show_unverified` off, `--results=verified` drops those, so a
scanner with no egress reports *zero* findings rather than failing loudly.
**If your runners cannot reach the public internet, set
`trufflehog_show_unverified: true`** so `unknown` results are still
reported (as low severity). (#110)

- **Behavioral:** a TruffleHog run that exits non-zero, or a missing
`trufflehog` binary, now **fails the run** instead of being reported as a
clean scan. Previously any non-zero exit was logged and turned into an empty
result, so a malformed exclude pattern or a broken install silently zeroed out
every secret finding while the run exited green — a scanner that could not
scan looked identical to a repository with no secrets. The error names the
exit code and TruffleHog's own stderr. This closes the last open item from
CE-347, whose other halves shipped in 2.2.1. (#110)
- `trufflehog_show_unverified` is now read through `coerce_bool` rather than
tested for truthiness. Only the environment loader coerces boolean params;
a Socket dashboard config is passed through verbatim and at higher priority,
so a dashboard-supplied string `"false"` was truthy and would have reported
unverified secrets to someone who explicitly turned them off. (#110)

### Changed
- `--include-detectors=all` is now passed unconditionally rather than only when
`trufflehog_show_unverified` is on, so detector selection no longer changes as a
side effect of that setting. TruffleHog already defaults to all detectors, so
this is a no-op in practice. (#110)
- Clarified TruffleHog parameter documentation: `trufflehog_exclude_dir` accepts
directory names, file names, and glob patterns (not just directories), matching
is case-sensitive, and excluded paths are removed from the scan entirely rather
than filtered from results. `trufflehog_show_unverified` is documented as
widening result types, not as toggling verification. (#110)

## [3.1.0] - 2026-09-02

### Added
Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ inputs:
required: false
default: ""
trufflehog_show_unverified:
description: "Show unverified secrets in TruffleHog results"
description: "Include unverified and unknown secrets in TruffleHog results; verification always runs, and by default only verified secrets are reported"
required: false
default: "false"
use_custom_sast_rules:
Expand Down
17 changes: 12 additions & 5 deletions docs/github-action.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,19 @@ Include these in your workflow's `jobs.<job_id>.permissions` section.
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
secret_scanning_enabled: 'true'
# Optional: exclude directories
trufflehog_exclude_dir: 'node_modules,vendor,dist'
# Optional: show unverified secrets
# Optional: exclude paths (directory names, file names, or globs)
trufflehog_exclude_dir: 'node_modules,vendor,dist,**/appsettings.*.json'
# Optional: report unverified and unknown secrets too (default: verified only)
trufflehog_show_unverified: 'true'
```

> **Secret verification runs on every scan and requires network egress.** By default only
> verified secrets are reported, and those are critical and blocking. TruffleHog confirms
> each candidate against third-party validation endpoints; when a runner cannot reach them
> the result is `unknown` and is dropped in the default verified-only mode. On air-gapped or
> proxied runners, set `trufflehog_show_unverified: 'true'` so `unknown` results are still
> reported as low-severity findings instead of silently disappearing.

**Container Scanning:**
```yaml
- uses: SocketDev/socket-basics@v3.1.0
Expand Down Expand Up @@ -849,8 +856,8 @@ See [`action.yml`](../action.yml) for the complete list of inputs.

**Security Scanning:**
- `secret_scanning_enabled` — Enable secret scanning
- `trufflehog_exclude_dir` — Directories to exclude
- `trufflehog_show_unverified` — Show unverified secrets
- `trufflehog_exclude_dir` — Comma-separated paths to exclude (directory names, file names, or globs)
- `trufflehog_show_unverified` — Include unverified and unknown secrets alongside verified ones
- `socket_tier_1_enabled` — Socket Tier 1 reachability

**Container Scanning (configuration surface):**
Expand Down
21 changes: 18 additions & 3 deletions docs/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,9 @@ socket-basics --disable-secrets
### `--exclude-dir EXCLUDE_DIR`
Comma-separated literal directory/file names or glob patterns to exclude from
secret scanning beneath the workspace root. Matching is case-sensitive. For
example, `**/appsettings.*.json` matches files at any directory depth.
example, `**/appsettings.*.json` matches files at any directory depth. Excluded
paths are removed from the scan entirely — they are not scanned for verified or
unverified secrets.

**Example:**
```bash
Expand All @@ -392,7 +394,20 @@ socket-basics --secrets --trufflehog-notify slack
```

### `--show-unverified`
Show unverified secrets in TruffleHog results (by default only verified secrets are shown).
Include unverified and unknown secrets in TruffleHog results. TruffleHog always performs
verification; this flag only widens which result types are reported. By default only
verified secrets are returned (`--results=verified`); with this flag, verified, unverified,
and unknown results are all returned (`--results=verified,unverified,unknown`).

Verified findings are reported as critical and block; unverified findings are reported as
low and do not block.

> **Verification makes live network requests.** TruffleHog validates candidate secrets
> against third-party endpoints (AWS, GitHub, Slack, and so on). If a runner cannot reach
> those endpoints, the result is classified as `unknown`, which is *not* returned in the
> default verified-only mode — an air-gapped scan will report zero findings rather than
> failing. Set `--show-unverified` on egress-restricted runners so `unknown` results are
> still reported.

**Example:**
```bash
Expand Down Expand Up @@ -660,7 +675,7 @@ You can provide configuration via a JSON file using `--config`:

"secrets_enabled": true,
"trufflehog_exclude_dir": "node_modules,vendor,dist,.git",
"show_unverified": false,
"trufflehog_show_unverified": false,

"socket_tier_1_enabled": true,
"socket_org": "your-org-slug",
Expand Down
2 changes: 1 addition & 1 deletion socket_basics/connectors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ connectors:
default: ""
- name: trufflehog_show_unverified
option: --show-unverified
description: "Show unverified secrets in TruffleHog results"
description: "Include unverified and unknown secrets in TruffleHog results; verification always runs, and by default only verified secrets are reported"
env_variable: INPUT_TRUFFLEHOG_SHOW_UNVERIFIED
type: bool
default: false
Expand Down
48 changes: 44 additions & 4 deletions socket_basics/core/connector/trufflehog/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

from ..base import BaseConnector

# coerce_bool lives in the config layer because the environment loader, a
# Socket dashboard config, and a JSON config each deliver booleans differently.
from ...config import coerce_bool

# Import individual notifier modules
from . import github_pr, slack, ms_teams, ms_sentinel, sumologic, console, jira, webhook, json_notifier

Expand Down Expand Up @@ -266,11 +270,28 @@ def scan(self) -> Dict[str, Any]:
except Exception:
changed_files = []

# Verification always runs so that findings carry a trustworthy
# Verified flag; the setting only controls which result types are
# returned. Detector selection is deliberately independent of it.
#
# coerce_bool, not truthiness: only the environment loader coerces
# bool params, while a Socket dashboard config is passed through
# verbatim at higher priority. A dashboard-supplied string "false"
# is truthy, and reading it as "on" would report unverified secrets
# to someone who explicitly asked for verified-only.
show_unverified = coerce_bool(
self.config.get('trufflehog_show_unverified'), False
)
results_filter = (
'verified,unverified,unknown' if show_unverified else 'verified'
)
Comment thread
cursor[bot] marked this conversation as resolved.

cmd = [
'trufflehog',
'filesystem',
'--json',
'--no-verification' if not self.config.get('trufflehog_show_unverified', False) else '--include-detectors=all'
'--include-detectors=all',
f'--results={results_filter}',
]

# TruffleHog accepts --exclude-paths only once and expects a file
Expand Down Expand Up @@ -315,8 +336,22 @@ def scan(self) -> Dict[str, Any]:
result = subprocess.run(cmd, capture_output=True, text=True)

if result.returncode != 0:
logger.error(f"Trufflehog failed: {result.stderr}")
return {}
# Fail closed. Returning {} here reports "no secrets found" and
# exits green, so a malformed exclude pattern or a broken
# install silently zeroes out every secret finding for the run.
# A scanner that cannot scan must not look like a clean scan.
# SystemExit is deliberate: the connector manager catches
# Exception, and this must not be downgraded to a skipped
# connector.
stderr = (result.stderr or '').strip()
detail = f": {stderr}" if stderr else ''
raise SystemExit(
f"TruffleHog exited {result.returncode} and scanned nothing"
f"{detail}\nSecret scanning results are incomplete, so the "
"run is failing rather than reporting a clean scan. Check "
"the exclude patterns in 'trufflehog_exclude_dir' and that "
"the trufflehog binary is working."
)

# Parse JSON output line by line
findings = []
Expand Down Expand Up @@ -374,7 +409,12 @@ def scan(self) -> Dict[str, Any]:
}

except FileNotFoundError:
logger.error("Trufflehog not found. Please install Trufflehog")
# Also fail closed: secret scanning was asked for and did not run.
raise SystemExit(
"TruffleHog is enabled but the 'trufflehog' binary was not "
"found, so no secret scanning ran. Install TruffleHog or use "
"the Socket Basics container image, which bundles it."
)
except Exception as e:
logger.error(f"Error running Trufflehog: {e}")
finally:
Expand Down
3 changes: 2 additions & 1 deletion tests/test_changed_files_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,8 @@ def record_run(cmd, *args, **kwargs):
"trufflehog",
"filesystem",
"--json",
"--no-verification",
"--include-detectors=all",
"--results=verified",
str(pr_repo),
]
]
Expand Down
7 changes: 6 additions & 1 deletion tests/test_trufflehog_excludes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import re
from types import SimpleNamespace

import pytest

from socket_basics.core.connector.trufflehog import TruffleHogScanner


Expand Down Expand Up @@ -488,6 +490,9 @@ def fake_run(command, **kwargs):
fake_run,
)

scanner.scan()
# A failed run surfaces rather than reporting a clean scan (CE-347); the
# temporary filter file must still be cleaned up on that path.
with pytest.raises(SystemExit):
scanner.scan()

assert not captured["exclude_path"].exists()
Loading