Tags: SocketDev/socket-basics
Tags
chore(release): 3.4.0 (#120) * chore(release): 3.4.0 Stamps [Unreleased] as [3.4.0], bumps the version files and uv.lock, and synchronizes the current-release references across README.md and docs/**. Minor rather than patch: #119 changes the snippet, detailed report, dataflow trace and description that every consumer of a finding reads, and adds the `redact` rule-metadata key. Also records the socketdev 3.5.0 -> 3.6.0 lockfile bump from #117, which merged without a changelog entry. * fix(redaction): treat plain-text-password as logic, not a credential The rule that fragment names matches password *handling* -- assigning request input to a password field, or comparing against one -- so its match is an expression rather than a literal. Running the literal pass on it reduced `user.password = request.form.get('password')` to a row of asterisks, which is the rule's main pattern and leaves nothing to act on. It belongs with `hardcoded-ip` and the password-policy rules, which the same comment already excludes for the same reason. A comparison against a hardcoded value is the one shape it covers that carries a credential, and that is what the `hardcoded-*` rules are for. Also folds a duplicated TestRedactMessage class into one. The second definition shadowed the first, so two message tests never ran. * fix(redaction): serve both shapes of the password-logic rule Dropping plain-text-password from the credential fragments fixed the over-masking but opened a hole: one of that rule's patterns is a comparison against a hardcoded string, and no hardcoded-* rule matches that shape, so `if user.password == "hunter2"` went into the facts file verbatim. Confirmed by scanning a file with exactly that line. The fragment goes back, and the over-masking is fixed where it belongs. An assigned value that calls something is an expression, not a bare credential, so it skips the unquoted fallback and the literal pass masks just the quoted parts. `user.password = request.form.get('password')` keeps its expression, the comparison value is masked, and a bare value with a trailing comment is still masked whole so a short credential cannot be partly revealed. * fix(redaction): bind the right operator and mask trailing comments Three defects in the unquoted-assignment fallback, found by working through the shapes the credential rules actually produce. A call matched anywhere in the value skipped masking entirely, so `password: hunter2 # see get_secret()` kept the credential. The check is now anchored: only a value that opens with a call is treated as an expression. The first operator on the line bound, so a type annotation won over the assignment after it and `password: str = "..."` was starred out whole rather than reaching the literal pass -- ordinary Python and TypeScript. The last operator now binds, and operators covered by a string literal are skipped so the `:` in `url = "https://..."` cannot bind either. That needs the literal spans, which one regex cannot express, so _split_assignment walks the matches. Masking the value left a comment beside it holding the plaintext, as in `password = get_secret() # real value is hunter2`. Text after an unquoted comment marker is now masked too. Also stops measuring an unquoted value together with whatever follows it. `password: hunter2 # plain comment` is long enough for a partial reveal even though `hunter2` is not, and it was rendering as `password: hunt...ment`. * fix(redaction): take the comment off first and mask per statement Two ways a credential stayed in the part of the line the value search never looked at. A comment can hold an operator later in the line than the real one. Because the comment was masked after the operator was chosen, that one bound and the value in front of it was left in the head: `password = hunter2 # see x = y` kept hunter2. The comment now comes off before anything else reads the line. A line can also carry more than one statement, and only one operator binds per statement, so `a = hunter2; password = x` masked the second value and left the first. Masking now runs per statement, split on separators outside string literals. * fix(redaction): measure literal spans over the snippet, not per line A literal can open on one line and close on another. Spans were computed per line, so a marker on a literal's second line read as a comment and the rest of that line was starred -- dropping the closing quote, after which the literal pass no longer matched and the opening line's value survived. Spans are now measured once over the whole snippet and consulted by absolute position. Two things fall out of that. A snippet is a slice of a file, so a literal can also never close. The quoted value branch deferred to the literal pass, which never matches an unterminated literal, so `password = "hunter2` was left untouched. It now defers only when the quote opens a span the pass can find, and masks the value whole otherwise. A multi-line literal body was measured as one value, so the head-and-tail reveal exposed the start of its first line. Each line of such a body is now masked whole, with the line breaks kept so the snippet still shows where the literal begins and ends. * fix(redaction): recognize string prefixes and interpolated bodies A prefixed opener such as r""" or f""" was not read as opening a literal, so the opening line was starred and its quotes were removed. Later lines were still measured against the original spans, which said they were inside a literal, so nothing masked them and the final literal pass no longer matched. The prefix is now part of the opener check. That alone left a partial reveal: literal text around an interpolation inflates the body past the reveal threshold, so f"{b}_SuperSecret123!" showed 123!. An interpolated body is masked whole, on the same reasoning as a multi-line one -- it is a block of content, not a single opaque value. * fix(redaction): do not defer on a spurious empty-literal match An unterminated triple-quoted value still produces a literal match: the engine backtracks past the triple alternative and reads the first two quotes as an empty string. That match was enough to send the value to the masking pass, which then covered only those two quotes, so the credential stayed in the snippet. Affects bare and prefixed openers alike. Deferring now requires a span that starts at the quote and holds both delimiters, which an empty match cannot satisfy. _STRING_LITERAL also gained triple-quoted alternatives so a terminated block matches once with its real body rather than as an empty string followed by a second literal. * fix(redaction): fail safe when string state is lost, and fuzz the invariant A generated-snippet sweep found three gaps the hand-written cases did not, all of them the same thing: masking depends on knowing where string literals start and end, and a truncated snippet can make that unknowable. An unterminated literal is not reached by the assignment fallback when the value sits in a comparison or a call argument, so it went to the masking pass, which cannot match it. A quote outside every matched span now marks the rest of the line as literal content. An unclosed triple-quoted block does not simply fail to match -- its first two quotes match as an empty string and the third pairs with any stray quote later, producing one long span that hides a real assignment on a later line. An odd count of triple delimiters now masks from the opener to the end. A " or ' literal cannot hold a raw newline in any language these rules cover, so a match that does is the same pairing artifact rather than a literal. Those spans are discarded; backticks and triple quotes keep theirs. tests/test_secret_redaction_fuzz.py generates the combinations rather than listing them, and asserts no credential survives, none is partly revealed, and non-credential snippets come through unchanged. 2,000,000 generated cases pass; 20,000 run in CI in about a second. * fix(redaction): mask to the end of a snippet once string state is lost Bugbot found that a credential on a continuation line survived, and extending the fuzzer to put the secret after the line break -- it had only ever put it before -- found a second case immediately. Both are the same thing: masking that stops at the opening line. Where a literal opens and its end is unknowable, everything after is inside it as far as any reader can tell, so masking now runs to the end of the snippet rather than the end of the line. The two ways state is lost -- a quote no surviving span covers, and an odd number of triple delimiters -- are handled together instead of separately. The second case was the opposite failure. Masking an unquoted value whole destroyed the opening quote of a literal that continued past the line, so the snippet-wide pass afterwards no longer matched and the rest of the literal was left alone. Such a value is now masked only up to the opener, and the pass takes the literal itself. Both generators put the secret on either side of a line break, so the shape is covered from here on. 1,000,000 generated cases pass. * fix(redaction): fail closed on ambiguous credential syntax
chore(release): 3.3.0 (#116) Release prep for 3.3.0, bundling #114 and #115. #115 adds the `--scan-all` / `--no-scan-all` CLI flags, so this is a minor bump rather than a patch. Bumps pyproject.toml, socket_basics/version.py, socket_basics/__init__.py, action.yml and uv.lock to 3.3.0, synchronizes 83 current-release references across README.md and docs/**, and stamps [Unreleased] as [3.3.0] - 2026-09-15. Also pins the Socket Python CLI to 2.9.0 in Dockerfile.heavy and app_tests/Dockerfile, ahead of that release publishing to PyPI.
chore(release): 3.2.0 (#113) Release prep for 3.2.0: bump every version-bearing file, refresh uv.lock, synchronize current-release references in README and docs/**, and stamp the [Unreleased] changelog section as [3.2.0] - 2026-09-10. Bundles #110 (TruffleHog verification and fail-closed scan errors), #111 (CLI/action input parity plus the documentation consistency pass) and #112 (Java SAST rule rewrite, and the Socket Python CLI 2.8.0 bump in the heavy and app-tests images, which landed with that PR). The changelog section was condensed and reorganized around a new "Upgrade notes" block, because four of these changes alter which findings a scan produces and the per-PR entries buried that. Two consequences of #110 were missing from the changelog entirely and are now stated: on the default path (trufflehog_show_unverified off) verified secrets become critical and blocking where previously no secret could block a run, and verification is a live check that sends candidates to third-party validation endpoints. The Java volume reduction is quoted as the benchmark doc's own -92% headline with its scoping caveat (~26% of the drop is new test and example path exclusions, not rule logic), rather than the -94.6% unique-findings-in- mature-libraries-only subset the per-PR entry used. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(release): 3.1.0 (#109) * docs(changelog): draft 3.1.0 entry for the bundled release Covers #97, #98, #104, #105 and #106. Internal-only changes (#104, #106) are collapsed into a short section; customer-facing changes keep the detail needed to plan an upgrade, including the behavioral change where an unresolvable changed_files scope now fails instead of scanning. * chore(deps): refresh Socket-owned tool pins Socket npm CLI 1.1.154 -> 1.1.165 across all three images, matching the current npm release. Socket Python CLI 2.6.3 -> 2.7.0 in the heavy and app-tests images. 2.7.0 is NOT published to PyPI yet (latest is 2.6.11), so this is scaffolding: the heavy and app-tests image builds and core-tool-watch both fail until it lands. Keep this commit separate so it can be dropped or held if the CLI release slips. The socketdev Python SDK is already current at 3.5.0, so no change. * chore(release): 3.1.0 Version metadata, uv.lock, CHANGELOG date stamp, and 74 current-release documentation references, via scripts/prep_release.py --version 3.1.0. * fix(app-tests): refresh socketsecurity index metadata on install This install pins an exact version, so a stale cached uv index response makes a freshly published release look like it does not exist. Use --refresh-package for just this package rather than --no-cache, which would discard the cache mount's benefit for bandit and built wheels. Dockerfile.heavy already passes --no-cache-dir on its pip equivalent. * test: assert the socketsecurity pin, not the RUN's formatting The assertion matched an exact literal, so it broke when the install gained a --refresh-package flag and a line continuation even though the version pin it guards was unchanged. Collapse continuations and match the ARG-pinned spec after 'uv tool install' instead. Verified the guard still fails for an unpinned spec and for a hardcoded version that bypasses the ARG.
chore(release): 3.0.0 (#102) * chore(release): 3.0.0 Version refs (version.py, __init__.py, pyproject.toml, action.yml image tag) and CHANGELOG entry only, per the release process. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * chore(release): regenerate uv.lock for 3.0.0 uv.lock records the project's own version; uv sync --frozen fails on the pyproject mismatch without the regen. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * chore(release): bump Socket CLI to 2.6.0 in the heavy image Version ref bump folded into the release PR (was briefly #103). Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * chore(release): bump socketdev SDK to 3.5.0 Constraint + lock only; the core-tool-watch typed-params migration remains in #99, which rebases on this. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * chore(release): bump Socket CLI to 2.6.3 in the heavy image Adopts the post-outage CLI release bundling the final pending PRs, so 3.0.0 ships a current pin without needing a back-to-back Basics release. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --------- Signed-off-by: lelia <2418071+lelia@users.noreply.github.com>
fix: support TruffleHog exclude patterns (#94) * fix: handle trufflehog exclude paths * fix: use absolute trufflehog scan targets * fix: complete trufflehog exclude handling * docs: clarify trufflehog exclude fix * fix: support trufflehog exclude globs * fix: complete trufflehog exclusions for v2.2.1 Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(trufflehog): Address review feedback Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --------- Signed-off-by: lelia <2418071+lelia@users.noreply.github.com>
ci: publish multi-arch Docker image variants (#85) * ci: publish multi-arch Docker images Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * ci: publish socket-basics heavy image Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * docs(changelog): add 2.1.0 release notes Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): harden Docker release publishing Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): address Docker publish review findings Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): harden Docker manifest publishing Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): publish heavy variant as tag suffix in the shared repo All image variants now publish to the single socket-basics repository per registry, distinguished by tag suffix (2.1.0 vs 2.1.0-heavy) instead of a separate socket-basics-heavy repository. This follows the standard Docker variant convention (like :slim/:alpine), requires no new Docker Hub repo, token rescoping, or GHCR package visibility changes, and makes retiring the POC variant trivial. - _docker-pipeline.yml: new push_name input decouples the registry repo from the local build/artifact name - publish-docker.yml: merge-manifests iterates variants with a tag_suffix, tags via metadata-action flavor suffix, and inspects both suffixed tags Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: move unreleased changelog entries out of the shipped 2.1.0 section v2.1.0 was released from main with different content; this PR's entries now sit under [Unreleased] and get stamped as 2.2.0 at release time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: correct 2.1.0 changelog date to actual release date (2026-07-22) The 2026-06-02 date reflected when the bundled commits were authored, not when v2.1.0 was actually tagged and released. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(release): prep v2.2.0 — stamp changelog, bump version files and action image ref Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: gate publishing on version files matching the release tag Restores the guarantee lost when .hooks/version-check.py was removed in #46: resolve-version now fails fast if version.py, pyproject.toml, or the action.yml image tag disagree with the tag being published. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(scripts): add prep_release.py for mechanical release-prep PRs One command bumps version.py, pyproject.toml, action.yml, refreshes uv.lock, and stamps the [Unreleased] changelog section — so the final release PR is a five-file diff that always satisfies the publish workflow's version gate. Validates everything before writing anything; a failure leaves the tree untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(release): sync __init__.py version and derive bumps from pyproject The sync_release_version.py check from main caught socket_basics/ __init__.py still at 2.0.3 — a duplicate version field prep_release.py didn't know about. prep_release.py now bumps only pyproject.toml (the canonical source) and delegates derived files to sync_release_version.py so the two scripts can never disagree. The publish version gate also checks __init__.py now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(core-tool-watch): opt into fail-closed purl batch semantics The batch purl endpoints default to fail-open: inputs with pending or failed resolution are silently omitted unless the caller opts in. Fresh pins (socketdev 3.3.0) fell into that omission path and tripped the unverified-pin guard with a misleading message. - purl.post now sends poll=true + timeoutSec=120 + alerts=true (extra kwargs pass through as query params on SDK 3.0.29 and 3.3.0) - client timeout raised 60->180s so the bounded server poll can finish - synthetic pendingScan/notFound rows are mapped to a status field before severity classification (never through MALWARE_ALERT_TYPES / CRITICAL_SEVERITIES) and fail closed with distinct, precise messages - OpenGrep's pkg:github coverage-gap exemption carries over: its pin now returns a notFound row instead of being omitted, and stays exempt - log the endpoint choice + org slug for forensics Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: changelog entry for core-tool-watch fail-closed purl fix Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(core-tool-watch): calibrate alert thresholds for the full alert set alerts=true exposed the complete informational alert firehose for the first time (the old fail-open responses carried no alert data, so the malware gate never actually saw alerts). Calibrated against real batch data from run 30504424787: - drop capability/heuristic signals from MALWARE_ALERT_TYPES: shellAccess fires on all four tools (security CLIs spawn subprocesses), gptMalware/gptSecurity/obfuscatedFile fire on the OpenGrep repo artifact (SAST engines bundle malicious-looking test fixtures by design) - hard-fail severity gate is critical-only; high-severity rows (cve on trivy, gpt heuristics) stay visible in the report for human review Verified: replaying the failing run's report through the new rules yields green while keeping true compromise signals fail-worthy. --------- Signed-off-by: lelia <2418071+lelia@users.noreply.github.com>
chore(deps): bundle Dependabot updates + harden dependency review wor… …kflows (#78) * chore(deps): bundle dependency updates + harden supply-chain review Bundles 8 open Dependabot PRs into one verified change and hardens the Dependabot config + dependency-review workflows, mirroring the work in socket-sdk-python#84 and socket-python-cli#207/#217. Adds a supply-chain watch for the four core OSS tools Dependabot cannot cleanly track. - uv.lock: idna 3.10->3.18 (CVE-2026-45409), pygments 2.19.2->2.20.0, pytest 8.4.2->9.0.3, urllib3 2.6.3->2.7.0 - _docker-pipeline.yml: bump 4 docker/* actions (setup-buildx, login, metadata, build-push) - dependabot.yml: add uv ecosystem, group every ecosystem into minor/patch + major bundles, scan composite actions - dependency-review.yml (was dependabot-review.yml): runs on every PR; free/enterprise sfw split; report artifacts; app_tests docker smoke - core-tool-watch.yml + scripts/check_core_tools.py: discover latest versions of opengrep/trufflehog/trivy/socketdev and score them through the Socket API (socketdev SDK purl.post); drift issue + report artifact - python-tests.yml: uv.lock drift guard Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ci): drop socket-firewall environment gate, add required coverage gate Mirroring the Python CLI/SDK used `environment: socket-firewall` to scope the SFW token, but that environment can carry a required-reviewers approval gate. Because the enterprise SFW check can't be a required status check (it would block Dependabot/fork PRs that only run the free edition), maintainers could merge without approving the deployment -- the meaningful check silently never ran, and approvers could rubber-stamp their own PRs. On the scheduled core-tool-watch job an approval gate would hang the cron run outright. - Remove `environment:` from python-sfw-smoke-enterprise and core-tool-watch; use a plain repo/org SOCKET_SFW_API_TOKEN (zizmor secrets-outside-env is already disabled here, so no lint cost). Job split still isolates the token to the enterprise job only. - Add always-on `dependency-review-gate` job: pass when no python deps changed, else require the free (Dependabot/fork) or enterprise (maintainer) smoke job to have succeeded. Mark THIS as the single required status check -- safe on every PR, no manual gate, no bypass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ci): scope SFW token via environment (no approval rule), harden gate Adopt the socket-python-cli#224 pattern uniformly. The environment was never the problem -- the required-reviewers approval RULE on it was. Keep the environment for secret scoping; forbid the rule. - Restore `environment: socket-firewall` on python-sfw-smoke-enterprise and the core-tool-watch analyze job so SOCKET_SFW_API_TOKEN is scoped to those jobs. Header documents that the environment must have NO reviewers rule, with the gh api command to enforce it (reviewers: null). - dependency-review-gate (Pattern 2 aggregator): now also needs docker-smoke-app-tests; fails on any failure/cancelled result (success and skipped pass) AND requires the trust-appropriate SFW edition to have succeeded when Python deps changed. Runs if: always() so the required context is always created -- no Pattern 1 bypass twin needed. Must land on main before being added to branch protection. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): degrade SFW enterprise to free when token absent; upload JSON report Live CI exposed two things on the now-enabled Actions: - socketdev/action firewall-enterprise HARD-ERRORS on an empty token (no silent fallback), so a trusted dep PR opened before the SOCKET_SFW_API_TOKEN secret exists fails and the required gate blocks merge. setup-sfw now resolves the effective mode and falls back to firewall-free when enterprise is requested without a token -- still a real supply-chain check, ships green today, auto-upgrades to enterprise the moment the secret is added. Token is read via env, never interpolated into the script. - socketdev/action writes a structured report to $SFW_JSON_REPORT_PATH; both smoke jobs now capture it and upload it alongside the tee'd log. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * feat(core-tool-watch): add Semgrep upstream proxy for OpenGrep Socket scoring OpenGrep ships as a GitHub-release binary that Socket has no data for under its pkg:github coordinate, so the watcher reported 'no data' for it. OpenGrep is a hard fork of Semgrep, so fall back to scoring the upstream Semgrep lineage (pkg:pypi/semgrep) as a project-health proxy. The proxy is report-only and never build-failing: it does not analyze OpenGrep's own release artifacts, so a Semgrep alert must not block an OpenGrep build. The pinned/latest verdicts show the proxy result labeled '(via semgrep upstream proxy)' when the primary coordinate has no data, and the JSON report records it under a separate 'proxy' key. The npm 'opengrep' package is a single-version squat (not the official distribution) and is deliberately not used as a coordinate. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * chore(deps): bundle 3 newly-filed Dependabot PRs (#81, #86, #87) - requests >=2.31.0 -> >=2.33.0 (#87); targeted 'uv lock --upgrade-package requests' resolves 2.34.2 (newer than Dependabot's 2.33.0) and pulls light-s3-client 0.0.40 -- the only two packages Dependabot's own PR touched. - actions/setup-python 6.2.0 -> 6.3.0 in python-tests.yml (#86, SHA verified against the v6.3.0 tag). The group's setup-buildx 4.1.0 bump is already in this branch. - docker/metadata-action 6.1.0 (#81) is already applied here (identical SHA) -- #81 is fully superseded, no code change. All three PRs to be closed manually as superseded by #78. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(core-tool-watch): address Cursor Bugbot findings on #78 Five hardening fixes flagged by Bugbot: 1. Critical alerts now fail-on-malware (was malware-only). Track any_critical separately and exit non-zero on malware OR critical, matching the documented intent; add a 'critical' GitHub output. 2. Pins are read from BOTH Dockerfiles. app_tests/Dockerfile pins the same core tools (trufflehog/trivy/opengrep) independently; the reader only saw the root Dockerfile, so a divergent app_tests bump went unscored. Tool.pinned is now a list of every distinct pinned version, all of which are scored. 3. Watch mode no longer fails on latest. Only PINNED (in-use) versions are fail-worthy; the discovered latest is scored for drift reporting only, so a scheduled watch can't go red on an upstream release we haven't adopted. 4. dependency-review-gate fails closed when inspect fails. A failed inspect left DEPS_CHANGED/IS_TRUSTED empty, so the coverage rules silently passed and a PR with dep changes could merge with no Socket Firewall run. Added Rule 0. 5. Socket API errors fail closed in build mode. A swallowed analyze_purls exception let --fail-on-malware exit 0 with pinned versions unverified; now a scoring error (token present) fails the run. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): address 2 more Bugbot findings on #78 1. Drift issue never opened (High). gh issue list --jq '.[0].number' prints the literal string 'null' when no open core-tool-drift issue exists; 'null' is non-empty in bash, so the first scheduled drift run would call 'gh issue edit null' instead of creating the issue. Use '// empty' so an absent issue yields an empty string and the create branch runs. 2. Tests ignored the lockfile (Medium). python-tests installed deps via 'pip install -e .[dev]' (a fresh resolution) while only asserting the lock separately, so tests could run against different versions than uv.lock. Switch to 'uv sync --locked --extra dev' + 'uv run --no-sync pytest' so tests run against exactly the locked set. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(ci): restore bare LICENSE to unblock CI (mirrors #88) Temporary: pull #88's fix into this branch so CI's merge-with-main ref builds. PR #79 renamed the license to LICENSE.md and deleted the empty LICENSE, but pyproject.toml and the Dockerfile still reference LICENSE, so hatchling and the Docker build fail against current main. This restores the PolyForm content into bare LICENSE (rename LICENSE.md -> LICENSE) and drops the now-moot !LICENSE.md .gitignore exception. Revert this commit and pull latest main once #88 lands there (main will then carry the identical fix). Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> * fix(core-tool-watch): per-event concurrency group + document Dependabot secret gap - Include github.event_name in the concurrency group so a merge to main can't cancel the in-flight weekly watch run (or vice versa). - Document that Dependabot-triggered pull_request runs never receive the environment-scoped SOCKET_SFW_API_TOKEN (only Dependabot secrets), so build-mode scoring silently degrades to drift-only there; note the 'gh secret set --app dependabot' mirror needed for pre-merge coverage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(deps-review): route Dependabot through sfw-enterprise; isolate core-tool-watch scan env Dependabot-triggered runs can read a *Dependabot-store* secret (set via 'gh secret set SOCKET_SFW_API_TOKEN --app dependabot'), so the free-tier routing for Dependabot -- a workaround for the assumption that its runs could never hold a token -- is gone: - dependency-review.yml: trusted == any in-repo (non-fork) PR, now including Dependabot. Its dep bumps get full org-policy (enterprise) enforcement; forks stay on the anonymous free edition. setup-sfw's existing empty-token fallback covers the window until the Dependabot secret mirror exists. - core-tool-watch.yml: sync the scan's Python env from the DEFAULT BRANCH lockfile (second checkout at .scan-env) so the token-holding step never imports packages bumped by the PR under review -- it only reads the PR's pins. Makes the Dependabot token mirror safe here too. - dependency-review.yml: import smokes use 'uv run --no-sync' so the post-firewall step can't re-sync outside sfw (Bugbot finding). - check_core_tools.py: docstring/help now honestly describe the strict fail thresholds (curated malware-class list + high/critical), which are intentional (Bugbot finding). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(core-tool-watch): sync scan env with --no-install-project The scan env checkout of main fails to build the socket-basics package editable while main carries the LICENSE.md rename breakage (#79, fix pending in #88). The scan only imports the dependencies (socketdev SDK), never socket_basics itself, so skip installing the project entirely -- also insulates this guard from any future main-side packaging breakage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(core-tool-watch): org-scoped purl endpoint + fail closed on empty API result Two related hardenings from external review of purl.post() usage: - Pass org_slug (resolved via client.org.get) when the installed SDK supports it: socketdev >= 3.1 (socket-sdk-python#76) deprecates the legacy POST /v0/purl in favor of POST /v0/orgs/{slug}/purl, and a future major may drop the legacy route. The pinned 3.0.29 predates the parameter, so it is signature-gated -- activates automatically when the scan env's lockfile bumps the SDK. - Raise on an empty purl.post result: the SDK swallows ANY non-200 (expired token, dropped endpoint, outage) into [], which previously flowed through as 'no data' verdicts and exit 0 -- fail-open. Every run scores coordinates Socket definitely has data for, so empty is an API failure; raising routes it into the existing scoring_error fail-closed path under --fail-on-malware. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(core-tool-watch): resolve org slug only when unambiguous Match socket-python-cli's get_org_id_slug() semantics: pass org_slug to purl.post only when the token maps to exactly one org; multi-org tokens fall back to the legacy endpoint with a notice rather than guessing and scoring under the wrong org's policies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(core-tool-watch): fail closed on unverified pinned coordinates Bugbot: a non-empty but incomplete Socket batch (or a pinned coordinate that never matches a returned analysis row) previously passed as 'no data' with exit 0 -- the guard could green-light a build without verifying every pin it exists to gate. Tools now declare socket_coverage (default True); with a token and a successful scoring pass, any covered pinned coordinate missing from the results is collected as unverified and fails a --fail-on-malware run, listing the exact coordinates. OpenGrep sets socket_coverage=False: its pkg:github coordinate is the documented no-data case with the report-only semgrep proxy, and must not perma-fail the guard. The unverified list is also surfaced in the JSON report. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
chore: fix release and updater script (#57) * chore: bump versions to v2.0.2, clean changelog, fix update script Signed-off-by: lelia <lelia@socket.dev> * chore: handle existing release edge case Signed-off-by: lelia <lelia@socket.dev> * chore: update PR template to remove temp instrux Signed-off-by: lelia <lelia@socket.dev> * chore: further template tweaks Signed-off-by: lelia <lelia@socket.dev> * fix: remove duplicate line from workflow Signed-off-by: lelia <lelia@socket.dev> --------- Signed-off-by: lelia <lelia@socket.dev>