Skip to content

Commit f0c9446

Browse files
committed
fix: honor changed_files from every config source and stop failing silently
1 parent 83d3801 commit f0c9446

10 files changed

Lines changed: 1493 additions & 147 deletions

File tree

CHANGELOG.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,52 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
88

99
## [Unreleased]
1010

11+
### Fixed
12+
13+
- `changed_files` is now honored no matter which way the config is built. It was
14+
only resolved inside `create_config_from_args()`, so `INPUT_CHANGED_FILES` was
15+
missing from the environment loader entirely (unlike `INPUT_SCAN_ALL` and
16+
`INPUT_SCAN_FILES`) and any config built another way scanned the whole
17+
repository. All sources now go through one resolver.
18+
- A raw `changed_files` string from a `--config` JSON file or a Socket dashboard
19+
config is now resolved against git. `"auto"` used to be stored verbatim and
20+
then iterated character by character, looking for files named `a`, `u`, `t`
21+
and `o`, which scoped the scan to nothing.
22+
- `scan_all` still overrides `changed_files`, but now logs a warning naming the
23+
scope it discarded. It can come from a Socket dashboard config rather than the
24+
workflow, so discarding the request silently made diff-only mode look broken.
25+
The warning also says that the override is partial: only the scanners that ask
26+
`get_scan_targets()` for their paths widen, while the secret and container
27+
scanners read `changed_files` directly and stay scoped, so setting both
28+
produces a mixed run.
29+
- Pull request base resolution now falls back to `pull_request.base.sha` and
30+
`pull_request.base.ref` from the GitHub event payload when `GITHUB_BASE_REF`
31+
is unset, which is the case on any trigger other than `pull_request` and
32+
`pull_request_target`. That covers `pull_request_review` and
33+
`pull_request_review_comment`, whose payloads carry a top-level
34+
`pull_request`. It cannot cover `issue_comment`, whose payload only has
35+
`issue.pull_request` (URLs, no base ref or sha), so that trigger now gets a
36+
warning naming itself and saying how to supply the base instead.
37+
- Changed-file detection now works inside the action's container. The scan runs
38+
as root over a workspace owned by the runner user, which git refuses with
39+
`detected dubious ownership`, so every diff failed and `changed_files: 'auto'`
40+
or `'pr'` resolved to nothing. Nothing in a workflow could fix it: setting
41+
`safe.directory` in a workflow step writes the runner's git config, not the
42+
container's. When git refuses, the git reads now trust that one workspace
43+
directory so the diff can run, and say so in the log. When git is not
44+
refusing — any ordinary local run — nothing is relaxed.
45+
- A scope request that cannot be honored now says why. Shallow checkouts
46+
(naming `fetch-depth: 0`), a workspace that is not a git repository, git
47+
refusing to read the repository, and a missing PR base each log a specific
48+
warning, and a scope that resolves to zero files warns that the scanners are
49+
being skipped. All of these previously returned an empty list in complete
50+
silence.
51+
- TruffleHog and Trivy no longer substitute their own staged-file scope when an
52+
explicit `changed_files` request resolved to nothing. Trivy's filesystem
53+
vulnerability scan also stops widening an empty scope back out to the whole
54+
workspace; it is the one scanner that builds its own path list instead of
55+
going through `get_scan_targets()`, so it needed the check twice.
56+
1157
## [2.2.1] - 2026-07-30
1258

1359
### Fixed

action.yml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,15 @@ inputs:
112112
description: >-
113113
Diff-only mode: scope every scanner (SAST/OpenGrep, secrets, containers)
114114
to changed files only, instead of the whole repository. Accepts a
115-
comma-separated file list, a commit hash, 'auto' (diffs against the PR
116-
base branch in CI, else staged changes), 'pr' (diff against
117-
GITHUB_BASE_REF), or 'current-commit'. For PR/'auto' modes, check out with
118-
actions/checkout fetch-depth: 0 so the base branch is available. When the
119-
diff resolves to no existing files (e.g. a delete-only PR) the scanners
120-
are skipped rather than scanning the whole repo.
115+
comma-separated file list, a commit hash, 'auto' (the PR base diff when a
116+
pull request base can be found, else staged changes), 'pr' (the PR base
117+
diff only), or 'current-commit'. The PR base comes from GITHUB_BASE_REF or
118+
from pull_request.base in the GitHub event payload. For PR/'auto' modes,
119+
check out with actions/checkout fetch-depth: 0 so the base branch is
120+
available. When the diff resolves to no existing files (e.g. a delete-only
121+
PR) the scanners are skipped rather than scanning the whole repo, and when
122+
the scope cannot be resolved at all the run logs a warning saying why.
123+
Note that scan_all overrides this input.
121124
required: false
122125
default: ""
123126
scan_files:

docs/github-action.md

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,9 @@ jobs:
311311

312312
`changed_files` accepts:
313313

314-
- `auto` — diff against the PR base branch in CI (`GITHUB_BASE_REF`), else staged changes
315-
- `pr` — diff against the PR base branch (`GITHUB_BASE_REF`)
314+
- `auto` — the PR base diff when a pull request base can be found, else staged changes
315+
- `pr` — the PR base diff, and nothing else
316+
- `current-commit` — files in the `HEAD` commit
316317
- a commit hash — files changed in that commit
317318
- a comma-separated file list — e.g. `src/app.py,src/utils.js`
318319

@@ -322,6 +323,43 @@ jobs:
322323
> nothing rather than falling back to the whole repo. To scan an explicit file
323324
> list regardless of git state, use the `scan_files` input instead.
324325

326+
### Checking that the scope took effect
327+
328+
Diff-only mode logs what it did. Look for these lines in the step output:
329+
330+
```text
331+
INFO Resolved PR diff base to 'origin/main'
332+
INFO Diff-only scan scoping requested (changed_files=auto): resolved 12 changed file(s)
333+
INFO Diff-only scan scoping active: 12 scan target(s) from 12 changed file(s)
334+
```
335+
336+
If the scope could not be applied, the run says why instead of quietly scanning
337+
everything or nothing:
338+
339+
| Warning you will see | What to do |
340+
|----------------------|------------|
341+
| `none of the candidate PR bases (...) could be resolved ... The checkout is shallow` | Add `fetch-depth: 0` to `actions/checkout` |
342+
| `no pull request base was found` | The trigger is not `pull_request`, so there is no base. Use `changed_files: 'current-commit'` or an explicit file list |
343+
| `is not a git repository` | Run `actions/checkout` before the scan step |
344+
| `git refused to read ... not the usual container ownership mismatch` | The checkout is damaged or incomplete. Re-run `actions/checkout`, or pass an explicit file list |
345+
| `scan_all and a changed-files scope ... are both set, and they disagree` | Unset `scan_all` — it can come from a Socket dashboard config, not just your workflow |
346+
| `resolved to zero files. The scanners will be SKIPPED` | The diff found nothing scannable. Combined with a warning above, it tells you the diff failed rather than the PR being empty |
347+
348+
You do not need `git config --global --add safe.directory` for this. The scan
349+
runs as root inside a container over a workspace owned by the runner user, and
350+
git normally refuses that with `detected dubious ownership`. When git refuses,
351+
the scan trusts that one workspace directory so the diff can run, and logs that
352+
it did — so no workflow change is needed. Setting `safe.directory` in a workflow
353+
step would not have helped anyway, because it writes the runner's git config
354+
rather than the container's. When git is not refusing, nothing is relaxed.
355+
356+
### Where the setting can come from
357+
358+
`changed_files` is honored identically from the action input, the
359+
`INPUT_CHANGED_FILES` environment variable, the `--changed-files` CLI flag, a
360+
`--config` JSON file, and a Socket dashboard config. `scan_all` outranks all of
361+
them; when it does, the run logs a warning naming the scope it discarded.
362+
325363
## PR Comment Customization
326364

327365
Socket Basics automatically posts enhanced PR comments with **smart defaults that work out of the box** — clickable file links, collapsible sections, syntax highlighting, CVE links, CVSS scores, and auto-labels are all enabled by default.

docs/parameters.md

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,74 @@ changed files only, the way Socket SCA Pull Request alerts behave. Accepts:
109109

110110
- a comma-separated file list (e.g. `src/app.py,src/utils.js`)
111111
- a commit hash — files changed in that commit
112-
- `auto` — the PR base-ref diff when running in a PR CI context
113-
(`GITHUB_BASE_REF` is set), otherwise staged (`--cached`) changes
114-
- `pr`diff against the PR base branch (`GITHUB_BASE_REF`)
112+
- `auto` — the PR base diff when a pull request base can be found, otherwise
113+
staged (`--cached`) changes
114+
- `pr` — the PR base diff, and nothing else
115115
- `current-commit` — files in the `HEAD` commit
116116

117+
The same value works from the CLI (`--changed-files`), the `changed_files`
118+
action input, the `INPUT_CHANGED_FILES` environment variable, a `--config` JSON
119+
file, and a Socket dashboard config. Whichever way it arrives, it is resolved
120+
against git once, in the same place.
121+
117122
Deletions are excluded from PR/`auto`/`pr` diffs so removed paths never become
118123
scan targets. When the diff resolves to no existing files (e.g. a delete-only
119124
PR), the scanners are skipped rather than falling back to scanning the whole
120125
repository. For PR/`auto`/`pr` modes, check out with full history (e.g.
121126
`actions/checkout` with `fetch-depth: 0`) so the base branch is available.
122127

128+
**Finding the pull request base.** `auto` and `pr` try, in order:
129+
130+
1. `GITHUB_BASE_REF` — set by GitHub on `pull_request` and
131+
`pull_request_target` triggers only
132+
2. `pull_request.base.sha` from the event payload at `GITHUB_EVENT_PATH` — an
133+
exact commit, so it works even when no remote-tracking branch exists
134+
3. `pull_request.base.ref` from the same payload
135+
136+
Each candidate is tried as `origin/<ref>` and then bare. If none resolves, the
137+
run logs a warning naming what it tried and why (shallow checkout, workspace is
138+
not a git repository, git refused to read the repository, no PR base at all) and
139+
the scanners are skipped. **A scope request that cannot be honored is never
140+
turned into a whole-repository scan, and it is never silent.**
141+
142+
Steps 2 and 3 cover the triggers whose payload carries a top-level
143+
`pull_request`: `pull_request`, `pull_request_target`, `pull_request_review`
144+
and `pull_request_review_comment`. They do **not** cover `issue_comment`. That
145+
payload has `issue.pull_request` instead, which is a set of URLs with no base
146+
ref or sha in it, so there is nothing to diff against without a GitHub API
147+
call. If you run the scan from a comment trigger, look the base up in the
148+
workflow and pass it in yourself:
149+
150+
```yaml
151+
- id: prbase
152+
run: echo "ref=$(gh pr view ${{ github.event.issue.number }} --json baseRefName -q .baseRefName)" >> "$GITHUB_OUTPUT"
153+
env:
154+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
155+
- uses: SocketDev/socket-basics@v2
156+
env:
157+
GITHUB_BASE_REF: ${{ steps.prbase.outputs.ref }}
158+
with:
159+
changed_files: 'auto'
160+
```
161+
162+
Otherwise the run warns that it was triggered by a comment on a pull request
163+
and that it cannot work the base out on its own.
164+
165+
**Socket Tier 1 reachability is not diff-scoped.** It runs `socket scan reach`
166+
over the whole workspace because reachability needs the full dependency graph,
167+
so a `changed_files` scope does not narrow it. That is unchanged behavior, and
168+
the scanners this setting does scope are SAST/OpenGrep, secrets and containers.
169+
170+
**`scan_all` outranks `changed_files` — but only for some scanners.** If
171+
`scan_all` is set — from `INPUT_SCAN_ALL`, a JSON config, or a Socket dashboard
172+
config — SAST scans the whole workspace and the changed-files scope is
173+
discarded. The secret and container scanners read `changed_files` off the config
174+
themselves rather than asking for scan targets, so they stay scoped to the
175+
changed files, and a run with both settings is a mix of the two. The run logs a
176+
warning saying exactly that, because `scan_all` often comes from a different
177+
place than the workflow that asked for diff-only scoping. Set one or the other,
178+
not both.
179+
123180
**Example:**
124181
```bash
125182
socket-basics --changed-files auto

0 commit comments

Comments
 (0)