Skip to content

Enforce Ruff lint and formatting in CI and pre-commit - #346

Open
lelia wants to merge 17 commits into
mainfrom
lelia/ruff-ci-precommit-mccabe
Open

lelia wants to merge 17 commits into
mainfrom
lelia/ruff-ci-precommit-mccabe

Conversation

@lelia

@lelia lelia commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Ruff linting and formatting now run as blocking checks on every pull request, with matching local commands and pre-commit hooks. The repository is clean under the expanded rule set and Ruff 0.17.4.

Enforcement

  • Add an unconditional Lint workflow running ruff check and ruff format --check across the repository, including hooks, benchmarks, and end-to-end tooling. Remove the duplicate lint job from Unit Tests.
  • Use the Ruff version pinned in pyproject.toml and resolved in uv.lock for CI, make lint, and pre-commit. These commands use --locked; hooks apply safe fixes and formatting for review before a commit succeeds.
  • Set C901 to 12 and PLR0913 to 8. The 24 existing over-limit functions have explicit suppressions; RUF100 rejects markers that become unnecessary. New functions should meet the limits.
  • Document rule exceptions and contributor commands. The SDK, socket-basics, and shared workflow rollout remain separate work.

Runtime and CI fixes

  • Mask credential fields, nested plugin settings, and integration URL paths in debug output. Slack configuration environment contents are reported only as set or unset. Cover nested settings, malformed URLs, IPv6 hosts, and both supported Slack webhook-list formats without changing the configuration used for delivery. Preserve the public ignore-command authorization policy in debug output while masking authorization credentials.
  • Remove only the timezone suffix from alert timestamps, preserving the rest of the timestamp.
  • Add 30-second timeouts to Slack, Teams, Jira, webhook, and GitLab notification requests. Contain Jira request failures so report files and the scan exit code remain available; error logs mask the endpoint and omit credential-bearing exception text.
  • Keep manifest upload validation active under python -O, send duplicate-package diagnostics to debug logging, and route configuration messages through the CLI logger.
  • Build manifest links from the configured SCM and resolved repository URL, without inferring the provider from a Socket report URL.
  • Correct local action paths, align build-tool pins with the project, and support Ruff's quote style in version extraction. CI-only changes do not require a package version bump. Move the release-version gate into .hooks/check_version.py, used by CI and tested against temporary Git repositories for packaged changes, documentation/CI exemptions, version mismatches, existing tags, and shared unreleased versions.

The branch includes current main through 866a462, preserving its pull request context, repository URL resolution, API error handling, and Coana downloader. Runtime fixes ship as 2.12.1. The changelog covers observable CLI behavior; contributor tooling, complexity limits, and release-version policy are documented in CONTRIBUTING.md and this description.

Validation

  • Ruff lint and format checks; all pre-commit hooks.
  • 836 unit/core tests on each of Python 3.11 and 3.12.
  • Wheel and sdist builds, twine check, and wheel metadata inspection.
  • Workflow validation, shell syntax checks, and the version-check script against the published PyPI version.
  • Fourth wall review of added comments, docstrings, configuration guidance, changelog, and this description.
  • All GitHub checks pass, including end-to-end scans, image reachability, and all four CodeQL analyses.

The current head's Python, GitHub Actions, JavaScript/TypeScript, and Rust CodeQL analyses report zero findings, with no open alerts on the PR head. Existing alerts on main have not been manually dismissed. Preview build jobs remain uncached and separate from publication.

Refs CE-451

lelia and others added 2 commits September 4, 2026 12:09
Ruff already ran in CI, but only as a job inside the Unit Tests workflow,
so it inherited that workflow's path filter and never saw .hooks/,
benchmarks/, or tests/e2e/. Move it to its own unconditional Lint
workflow, which also keeps it usable as a required status check.

Add ruff to pre-commit so violations surface before CI. The hook runs
ruff out of the project environment rather than the upstream mirror, so
the version stays pinned in one place; Dependabot has no pre-commit
ecosystem and would never update a mirror's rev.

Expand the rule set beyond E/F/I to cover bug classes that matter for a
CLI other people run in their pipelines, and fix every resulting
violation so the baseline is clean rather than suppressed.

Behaviour changes worth calling out:

- Package.created_at used str.strip(" (Coordinated Universal Time)"),
  which treats its argument as a set of characters, not a suffix. It ate
  a leading "T" from "Tue ..." and a trailing "T" from timestamps that
  carried no suffix at all. Now uses removesuffix.
- Every requests call in the plugins and the GitLab client now passes an
  explicit timeout. requests blocks forever by default, so a hung
  notification could wedge the pipeline the CLI reports into.
- Two asserts became real checks. assert is stripped under python -O, so
  neither guard survived an optimised interpreter.
- config.py logs through the socketcli logger instead of the root
  logger, so its messages honour the configured level and format.
- A stray debug print in the SBOM artifact loop became a log.debug call;
  it was writing to stdout, which carries machine-readable output.
- Closures defined inside loops in alert_selection and messages were
  hoisted and now take their inputs explicitly.

Complexity is bounded by C901 (max 12) and PLR0913 (max 8). The 20
functions over the limit today carry an explicit noqa; RUF100 fails the
build once a suppression goes stale, so the list can only shrink.

E501 and W291/W293 are left to ruff format rather than duplicated in the
linter: everything the formatter cannot reflow is a string literal, and
the PR-comment markup depends on trailing double-spaces as Markdown
hard line breaks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lelia
lelia requested a review from a team as a code owner September 7, 2026 18:20
@lelia
lelia temporarily deployed to socket-firewall September 7, 2026 18:20 — with GitHub Actions Inactive
Comment thread socketsecurity/core/messages.py Fixed
`ruff format` normalises string quotes to double, so `__version__` in
socketsecurity/__init__.py went from single to double quotes. Five places
parsed or rewrote that line assuming single quotes:

- version-check.yml stripped only `'`, so it read the version as `"2.7.2"`
  (quotes included) and failed to parse it. This is what broke on the PR.
- build_container.sh and build_container_flexible.sh would have produced a
  Docker tag containing literal quote characters.
- deploy-test-pypi.sh both read the version and rewrote it with a sed that
  matched single quotes only, so the rewrite would silently no-op.
- .hooks/sync_version.py read either quote style but always wrote single
  quotes, so it and the formatter would have rewritten the same line back
  and forth on every commit.

Readers now strip both quote characters and the hook writes double quotes
to match the formatter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lelia
lelia temporarily deployed to socket-firewall September 7, 2026 18:23 — with GitHub Actions Inactive
@lelia lelia changed the title Enforce ruff in CI and pre-commit, bound complexity, and clear the lint baseline Enforce ruff in CI + precommit, bound complexity, and clear lint baseline Sep 7, 2026
@lelia
lelia marked this pull request as draft September 7, 2026 18:27
CodeQL flagged `"github.com" in diff_url` as incomplete URL substring
sanitization. Looking at what diff_url actually holds makes the finding
more interesting than a sanitization gap.

diff_url is always a Socket dashboard link, built in Core as
`https://socket.dev/dashboard/org/{org_slug}/diff/...` (or the equivalent
sbom URL). Its host is always socket.dev and it carries no SCM
information -- which is exactly what the comment three lines below the
check already said. The only variable part is the org slug, so the sniff
could only ever fire when a Socket org slug happened to contain "github",
"gitlab" or "bitbucket". Such an org got a link to a repository host it
may not use; everyone else fell through to the Socket file view.

The branch was also almost unreachable: CliConfig declares `scm` with a
default of "api", so `hasattr(config, "scm")` is true for every real
config and the elif never runs. It was observable only for a config
object carrying `repo` but no `scm`, since the URL builders all require a
truthy config -- with `config=None` the sniffed value was computed and
then discarded.

Replaced with `getattr(config, "scm", None) or "api"`, which handles a
missing config, a config without the attribute, and an empty value.

Adds tests for get_manifest_file_url, which had none: GitHub, GitHub
Enterprise, GitLab, self-hosted GitLab, Bitbucket, the Socket fallback,
build-agent prefix stripping, and multi-manifest paths. The three
org-slug cases are regression guards, confirmed to fail against the old
implementation.

Removing the dead branch drops the function under the complexity limit,
so RUF100 required its `# noqa: C901` be removed. The backlog is now 19.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lelia
lelia temporarily deployed to socket-firewall September 7, 2026 18:49 — with GitHub Actions Inactive
lelia and others added 3 commits September 8, 2026 17:18
…it-mccabe

# Conflicts:
#	CHANGELOG.md
#	socketsecurity/__init__.py
CodeQL reported py/clear-text-logging-sensitive-data at output.py:127,
the `logger.info(json.dumps(...))` in output_console_json. That line is a
false positive -- neither build_json_report nor build_fossa_report_payload
copies a credential into the payload; both pick named fields. CodeQL taints
anything derived from self.config because CliConfig declares api_token.

Chasing it did turn up three real leaks:

- socketcli logged `config.to_dict()` at debug level, and to_dict() is
  asdict(), so `--debug` printed the Socket API token in clear text. In CI
  that lands in the job log, which is retained, pasted into support tickets
  and world-readable for public repositories.
- The Slack plugin logged the full webhook URL twice, once unconditionally
  at debug level. A webhook URL is a bearer credential -- anyone holding it
  can post into the customer's channel.
- output.py logged the configured webhook URL in its Slack debug block.

The Slack sites matter more than the token one: they run inside the
StreamingLogs context, whose upload handler has no level filter and whose
loggers are forced to DEBUG, so those records are shipped to Socket. The
config line runs before streaming attaches, so it stayed local.

Adds socketsecurity/redaction.py: redact_mapping masks values whose field
name looks credential-bearing, matching on the name so a field added later
is covered without anyone remembering. redact_url keeps a webhook's scheme
and host -- which is what the debug line was for -- and drops the secret
path and any userinfo. Unset values are left alone, since "no token
configured" is useful and is not a secret.

CliConfig.to_dict() still returns real values; to_redacted_dict() is the
logging view. A serialiser that silently dropped the token would be its own
bug.

The six actions/cache-poisoning alerts are inert: nothing in this
repository uses actions/cache or a `cache:` input, so there is no cache to
poison. They are not fixed, they are documented -- pr-preview.yml's build
job now says why caching must never be added there, since under
workflow_dispatch it executes untrusted PR code in the default branch's
cache scope. Recommend dismissing them rather than leaving them open.

TRY400/TRY401 move from "not selected" into `ignore` so the decision
survives a future family-wide selection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lelia lelia changed the title Enforce ruff in CI + precommit, bound complexity, and clear lint baseline Enforce ruff lint + format in CI and pre-commit, clear the baseline, and fix the CodeQL findings Sep 8, 2026
@lelia
lelia temporarily deployed to socket-firewall September 8, 2026 21:28 — with GitHub Actions Inactive
lelia and others added 2 commits September 9, 2026 13:56
…ch pins

Brings in #348 and #349, which move httpx2 and httpcore2 to 2.12.0 and
close five Dependabot advisories (GHSA-8xx6-hgc6-gc2m and
GHSA-7mj9-2mp8-4m2p high, three medium).

No CLI code change is needed for these, and the reason is worth recording:
httpx2 is not a runtime dependency. It arrives transitively through
`hatch` in the dev extra, so it is absent from the published wheel's
Requires-Dist and never reaches anyone installing socketsecurity from
PyPI. Nothing under socketsecurity/ imports httpx or httpcore, and the
only hatch commands this repository runs are `hatch version` and
`hatch build`, neither of which performs HTTP. The advisories describe
build-tooling exposure on developer machines and CI runners, not shipped
surface.

Verified against the release path rather than assumed: hatch version,
hatch build, twine check, and a no-deps wheel install with a bytecode
compile all pass on 2.12.0, as do the 568 unit and core tests.

Separately, the same investigation turned up three different pins for the
same build tooling: build-system.requires wanted hatchling 1.32.0, the dev
extra pulled 1.28.0 via hatch 1.18.0, and .github/actions/setup-hatch
installed hatchling 1.27.0 with hatch 1.14.0 -- so CI ran an older hatch
than local development. The artifact was never affected, because
`hatch build` resolves the backend in an isolated environment from
build-system.requires and the wheel records `Generator: hatchling 1.32.0`.
The composite action now matches the other two, verified by building in a
clean virtualenv with the new pins.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lelia
lelia temporarily deployed to socket-firewall September 9, 2026 17:59 — with GitHub Actions Inactive
lelia and others added 2 commits September 9, 2026 20:05
#331 added this workflow to its own paths filter so an edit to the check gets
exercised by the PR making it. That had the side effect of demanding a version
bump from CI-only PRs, and now that 2.8.0 is published it fails outright: a PR
touching only version-check.yml sits at the published version with nothing to
bump, and is told to bump it.

The bump is now enforced only when the PR touches socketsecurity/,
pyproject.toml or uv.lock. The comparison still runs and reports its verdict
either way, so the check stays exercised without dictating a release. Matches
the gating already on socket-sdk-python.

This branch predates #331, so the file is taken from main -- picking up the
PyPI-only floor and the pyproject/__init__ consistency assertion -- with this
branch's own tweak reapplied on top: the version literal is stripped of both
quote styles, since ruff format rewrites __init__.py to double quotes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
main shipped 2.8.0 (#325 monorepo scan diagnostics and API exit codes, #331
Slack severity normalization and the socketdev 3.6.0 bump) while this branch
was open, so the 2.7.3 it targeted is now behind the published release.

Conflict resolution, in every case keeping main's shipped behaviour and this
branch's formatting and fixes:

- slack.py: kept the "middle" -> "medium" normalization from #331, restyled to
  double quotes. Losing it would have silently reintroduced the wrong Slack
  severity counts.
- socketcli.py: kept #325's _log_scan_mode_fallback diagnostics and the
  force_api_mode branch; this branch's side was the older logic reformatted.
- core/__init__.py: kept #325's _log_scan_configuration helper and its call
  sites, and the switch from sys.exit to raise so API failures reach the
  configured infrastructure exit code. Kept this branch's contextlib.suppress
  cleanup and modern annotations, and modernized the helper's own signature
  since this branch drops the typing.List/Optional imports.
- version-check.yml: already assembled from main plus the enforcement gating
  and this branch's both-quote version strip.
- CHANGELOG: this branch's section is now 2.8.1, above the shipped 2.8.0.

uv.lock regenerated from the merged pyproject with the pinned uv 0.12.8.
ruff check and ruff format are both clean; 584 passed, 2 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lelia
lelia temporarily deployed to socket-firewall September 11, 2026 15:35 — with GitHub Actions Inactive
@lelia
lelia marked this pull request as ready for review September 11, 2026 15:42
@lelia
lelia temporarily deployed to socket-firewall September 11, 2026 15:42 — with GitHub Actions Inactive
@lelia lelia changed the title Enforce ruff lint + format in CI and pre-commit, clear the baseline, and fix the CodeQL findings Enforce ruff lint / format in CI + pre-commit, clear the baseline, fix CodeQL findings Sep 11, 2026
@lelia
lelia marked this pull request as draft September 11, 2026 15:54
The changelog entry and the module and test docstrings spelled out the
failure mode in enough detail to serve as a lookup guide, on a public
repository, before the fix has shipped. Reworded to describe the
behaviour -- configuration values and integration URLs are redacted in
log output -- without the specifics.

No functional change; redaction behaviour and tests are untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lelia
lelia temporarily deployed to socket-firewall September 11, 2026 15:55 — with GitHub Actions Inactive
@lelia lelia changed the title Enforce ruff lint / format in CI + pre-commit, clear the baseline, fix CodeQL findings Enforce Ruff lint and formatting in CI and pre-commit Nov 14, 2026
@lelia
lelia deployed to socket-firewall November 14, 2026 01:57 — with GitHub Actions Active
@lelia
lelia marked this pull request as ready for review November 14, 2026 02:23
@lelia
lelia deployed to socket-firewall November 14, 2026 02:23 — with GitHub Actions Active
@cursor

cursor Bot commented Nov 14, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Substantial CLI and config-path changes (redaction, notification timeouts, logging) ship in a patch release alongside broad lint refactors; CI behavior for version bumps also changes for non-packaging PRs.

Overview
Releases socketsecurity 2.12.1 with contributor-facing lint enforcement and several CLI reliability and safety fixes.

Lint and release tooling: Ruff now runs repo-wide on every PR via a dedicated Lint workflow (removed from python-tests), with matching make lint / make hooks and pre-commit hooks using the locked dev extra. pyproject.toml expands the Ruff rule set (complexity limits, security checks, self-cleaning # noqa via RUF100). Hatch/Hatchling pins align across CI and the build backend. Version-check logic moves into .hooks/check_version.py and only fails version rules when packaged paths change; CI-only PRs can pass without a bump. GitHub Actions fix local composite action references from $/.github/... to ./.github/....

2.12.1 runtime behavior: Debug output redacts credentials and sensitive URL paths (CliConfig.to_redacted_dict()), while ignore-command authorization policy stays visible. Notification plugins use a 30s HTTP timeout; Jira errors no longer abort report writing or change exit codes. Alert timestamps drop only the timezone suffix; manifest links respect configured SCM; duplicate SBOM messages and config warnings go through the CLI logger; manifest upload validation still runs under python -O.

Most application diffs are Ruff-driven style and typing cleanup (exit → sys.exit, modern list[str] annotations, formatting).

Reviewed by Cursor Bugbot for commit 8660622. Configure here.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread socketsecurity/redaction.py Resolved
@lelia
lelia deployed to socket-firewall November 14, 2026 02:26 — with GitHub Actions Active
@lelia
lelia deployed to socket-firewall November 14, 2026 02:38 — with GitHub Actions Active
@lelia

lelia commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread CHANGELOG.md Resolved
@lelia
lelia deployed to socket-firewall November 14, 2026 03:31 — with GitHub Actions Active
@lelia
lelia deployed to socket-firewall November 14, 2026 03:50 — with GitHub Actions Active
@lelia

lelia commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 8660622. Configure here.

This branch was successfully deployed

1 active deployment
socket-firewall — 8660622b Deployed Nov 14, 2026 by lelia via python-sfw-smoke-enterprise #602
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants