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

## [Unreleased]

### Fixed

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

## [2.2.1] - 2026-07-30

### Fixed
Expand Down
15 changes: 9 additions & 6 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,15 @@ inputs:
description: >-
Diff-only mode: scope every scanner (SAST/OpenGrep, secrets, containers)
to changed files only, instead of the whole repository. Accepts a
comma-separated file list, a commit hash, 'auto' (diffs against the PR
base branch in CI, else staged changes), 'pr' (diff against
GITHUB_BASE_REF), or 'current-commit'. For PR/'auto' modes, check out with
actions/checkout fetch-depth: 0 so the base branch is available. When the
diff resolves to no existing files (e.g. a delete-only PR) the scanners
are skipped rather than scanning the whole repo.
comma-separated file list, a commit hash, 'auto' (the PR base diff when a
pull request base can be found, else staged changes), 'pr' (the PR base
diff only), or 'current-commit'. The PR base comes from GITHUB_BASE_REF or
from pull_request.base in the GitHub event payload. For PR/'auto' modes,
check out with actions/checkout fetch-depth: 0 so the base branch is
available. When the diff resolves to no existing files (e.g. a delete-only
PR) the scanners are skipped rather than scanning the whole repo, and when
the scope cannot be resolved at all the run logs a warning saying why.
Note that scan_all overrides this input.
required: false
default: ""
scan_files:
Expand Down
42 changes: 40 additions & 2 deletions docs/github-action.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,9 @@ jobs:

`changed_files` accepts:

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

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

### Checking that the scope took effect

Diff-only mode logs what it did. Look for these lines in the step output:

```text
INFO Resolved PR diff base to 'origin/main'
INFO Diff-only scan scoping requested (changed_files=auto): resolved 12 changed file(s)
INFO Diff-only scan scoping active: 12 scan target(s) from 12 changed file(s)
```

If the scope could not be applied, the run says why instead of quietly scanning
everything or nothing:

| Warning you will see | What to do |
|----------------------|------------|
| `none of the candidate PR bases (...) could be resolved ... The checkout is shallow` | Add `fetch-depth: 0` to `actions/checkout` |
| `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 |
| `is not a git repository` | Run `actions/checkout` before the scan step |
| `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 |
| `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 |
| `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 |

You do not need `git config --global --add safe.directory` for this. The scan
runs as root inside a container over a workspace owned by the runner user, and
git normally refuses that with `detected dubious ownership`. When git refuses,
the scan trusts that one workspace directory so the diff can run, and logs that
it did — so no workflow change is needed. Setting `safe.directory` in a workflow
step would not have helped anyway, because it writes the runner's git config
rather than the container's. When git is not refusing, nothing is relaxed.

### Where the setting can come from

`changed_files` is honored identically from the action input, the
`INPUT_CHANGED_FILES` environment variable, the `--changed-files` CLI flag, a
`--config` JSON file, and a Socket dashboard config. `scan_all` outranks all of
them; when it does, the run logs a warning naming the scope it discarded.

## PR Comment Customization

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.
Expand Down
63 changes: 60 additions & 3 deletions docs/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,74 @@ changed files only, the way Socket SCA Pull Request alerts behave. Accepts:

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

The same value works from the CLI (`--changed-files`), the `changed_files`
action input, the `INPUT_CHANGED_FILES` environment variable, a `--config` JSON
file, and a Socket dashboard config. Whichever way it arrives, it is resolved
against git once, in the same place.

Deletions are excluded from PR/`auto`/`pr` diffs so removed paths never become
scan targets. When the diff resolves to no existing files (e.g. a delete-only
PR), the scanners are skipped rather than falling back to scanning the whole
repository. For PR/`auto`/`pr` modes, check out with full history (e.g.
`actions/checkout` with `fetch-depth: 0`) so the base branch is available.

**Finding the pull request base.** `auto` and `pr` try, in order:

1. `GITHUB_BASE_REF` — set by GitHub on `pull_request` and
`pull_request_target` triggers only
2. `pull_request.base.sha` from the event payload at `GITHUB_EVENT_PATH` — an
exact commit, so it works even when no remote-tracking branch exists
3. `pull_request.base.ref` from the same payload

Each candidate is tried as `origin/<ref>` and then bare. If none resolves, the
run logs a warning naming what it tried and why (shallow checkout, workspace is
not a git repository, git refused to read the repository, no PR base at all) and
the scanners are skipped. **A scope request that cannot be honored is never
turned into a whole-repository scan, and it is never silent.**

Steps 2 and 3 cover the triggers whose payload carries a top-level
`pull_request`: `pull_request`, `pull_request_target`, `pull_request_review`
and `pull_request_review_comment`. They do **not** cover `issue_comment`. That
payload has `issue.pull_request` instead, which is a set of URLs with no base
ref or sha in it, so there is nothing to diff against without a GitHub API
call. If you run the scan from a comment trigger, look the base up in the
workflow and pass it in yourself:

```yaml
- id: prbase
run: echo "ref=$(gh pr view ${{ github.event.issue.number }} --json baseRefName -q .baseRefName)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: SocketDev/socket-basics@v2
env:
GITHUB_BASE_REF: ${{ steps.prbase.outputs.ref }}
with:
changed_files: 'auto'
```

Otherwise the run warns that it was triggered by a comment on a pull request
and that it cannot work the base out on its own.

**Socket Tier 1 reachability is not diff-scoped.** It runs `socket scan reach`
over the whole workspace because reachability needs the full dependency graph,
so a `changed_files` scope does not narrow it. That is unchanged behavior, and
the scanners this setting does scope are SAST/OpenGrep, secrets and containers.

**`scan_all` outranks `changed_files` — but only for some scanners.** If
`scan_all` is set — from `INPUT_SCAN_ALL`, a JSON config, or a Socket dashboard
config — SAST scans the whole workspace and the changed-files scope is
discarded. The secret and container scanners read `changed_files` off the config
themselves rather than asking for scan targets, so they stay scoped to the
changed files, and a run with both settings is a mix of the two. The run logs a
warning saying exactly that, because `scan_all` often comes from a different
place than the workflow that asked for diff-only scoping. Set one or the other,
not both.

**Example:**
```bash
socket-basics --changed-files auto
Expand Down
Loading