diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 14af8789..d7c11531 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -89,6 +89,13 @@ updates: # package it installs. Those move together - a semgrep release raises the # floor of what it needs - and one pull request per package would leave # each of them red against the others' old pins. + # + # Grouping alone was not enough. Dependabot raised every line of the list on + # its own, past what semgrep allows, and pip refused the set (#184: boltons + # 26.2.0 against semgrep's boltons~=21.0). Since 2026-10-15 the list is + # compiled from requirements-semgrep.in by pip-compile, and an .in file in + # the directory makes Dependabot run pip-compile itself, so what it proposes + # is a set pip resolved. How to compile it by hand is written in the .in file. - package-ecosystem: pip directory: "/.github" schedule: diff --git a/.github/requirements-semgrep.in b/.github/requirements-semgrep.in new file mode 100644 index 00000000..39ca490d --- /dev/null +++ b/.github/requirements-semgrep.in @@ -0,0 +1,46 @@ +# The scanner CI runs, pinned, in a file a bot can edit. +# +# It lived as `pip install semgrep==X` inside a workflow step until 2026-09-02, +# and that is a place nothing watches. Dependabot reads go.mod for Go modules +# and `uses:` for actions, and it does not read shell commands - measured on +# this repository, thirty pull requests, none of them about a pinned tool. The +# cost of that was paid on 2026-09-01: staticcheck sat on a version that could +# not read Go 1.27 at all, and nobody had missed a warning, because there was +# never going to be one. +# +# A requirements file is an ecosystem Dependabot does read, so a new semgrep +# release now arrives as a pull request against THIS file. It can, because this +# file is not a workflow: a token cannot push changes under .github/workflows, +# which is why the three Go tools next door still cannot be handled this way. +# Written up as O170. +# +# Pinned rather than floating for the reason the workflow gives: an unpinned +# analyser turns somebody else's release into a red build on a commit that +# changed nothing. +# +# Licence read from the pinned version rather than recalled, on 2026-08-27: +# LGPL-2.1-or-later, stated by the wheel metadata as a License-Expression and +# carried in full beside it. Read again for 1.180.0 on 2026-10-14, the same. +# It analyses the tree and is never linked into either binary. +# +# This is the one line a person writes. requirements-semgrep.txt next to it is +# every package that line installs, pinned, and it is compiled from this file +# by pip-compile rather than written by hand. With the one line alone the +# vulnerability scanner has to guess what pip would pick, and it guessed wrong: +# measured 2026-10-14, a scan of "semgrep==1.180.0" reported eleven advisories +# against versions pip on Python 3.14 never installs. +# +# Compiled rather than frozen since 2026-10-15. The list was `pip freeze` of a +# clean install, and Dependabot raised each of its lines on its own - pull +# request #184 moved 21 of them, boltons to 26.2.0 among them, while semgrep +# 1.180.0 asks for boltons~=21.0, and pip refused the whole set. When an .in +# file is present Dependabot runs pip-compile itself, so what it proposes is a +# set pip can install. Compiled that day, the list came out the same 66 +# versions the freeze had, which is also why #184 had nothing to give: every +# one of its 21 versions is outside what semgrep allows. +# +# To compile it by hand, from the root of the repository, with the Python the +# job uses and the pip-tools Dependabot runs: +# +# docker run --rm -v "$PWD/.github:/w" -w /w python:3.14-slim sh -c "pip install -q pip==26.1.1 pip-tools==7.5.3 && pip-compile --strip-extras --no-emit-index-url --output-file=requirements-semgrep.txt requirements-semgrep.in" +semgrep==1.180.0 diff --git a/.github/requirements-semgrep.txt b/.github/requirements-semgrep.txt index 13b968be..9bdf43ae 100644 --- a/.github/requirements-semgrep.txt +++ b/.github/requirements-semgrep.txt @@ -1,101 +1,212 @@ -# The scanner CI runs, pinned, in a file a bot can edit. # -# It lived as `pip install semgrep==X` inside a workflow step until 2026-09-02, -# and that is a place nothing watches. Dependabot reads go.mod for Go modules -# and `uses:` for actions, and it does not read shell commands - measured on -# this repository, thirty pull requests, none of them about a pinned tool. The -# cost of that was paid on 2026-09-01: staticcheck sat on a version that could -# not read Go 1.27 at all, and nobody had missed a warning, because there was -# never going to be one. +# This file is autogenerated by pip-compile with Python 3.14 +# by the following command: # -# A requirements file is an ecosystem Dependabot does read, so a new semgrep -# release now arrives as a pull request against THIS file. It can, because this -# file is not a workflow: a token cannot push changes under .github/workflows, -# which is why the three Go tools next door still cannot be handled this way. -# Written up as O170. +# pip-compile --no-emit-index-url --no-index --output-file=requirements-semgrep.txt --strip-extras requirements-semgrep.in # -# Pinned rather than floating for the reason the workflow gives: an unpinned -# analyser turns somebody else's release into a red build on a commit that -# changed nothing. -# -# Licence read from the pinned version rather than recalled, on 2026-08-27: -# LGPL-2.1-or-later, stated by the wheel metadata as a License-Expression and -# carried in full beside it. Read again for 1.180.0 on 2026-10-14, the same. -# It analyses the tree and is never linked into either binary. -# -# Every package it installs is pinned below, not only semgrep, since -# 2026-10-14. With the one line alone the vulnerability scanner has to guess -# what pip would pick, and it guessed wrong: measured that day, a scan of -# "semgrep==1.180.0" reported eleven advisories against python-multipart -# 0.0.9, anyio 4.9.0 and idna 3.9.0, while pip on Python 3.14 installed -# 0.0.32, 4.15.1 and 3.20, and a scan of what it really installed found -# nothing. The list is `pip freeze` of a clean install on Linux with the -# Python the job uses, which is the only place it is installed. Dependabot -# reads every line, and carries them in one pull request - see the group in -# dependabot.yml. annotated-types==0.8.0 + # via pydantic anyio==4.15.1 + # via + # httpx + # mcp + # sse-starlette + # starlette attrs==26.1.0 + # via + # glom + # jsonschema + # referencing + # semgrep boltons==21.0.0 + # via + # face + # glom + # semgrep bracex==3.1 + # via wcmatch certifi==2026.7.22 + # via + # httpcore + # httpx + # requests cffi==2.1.1 + # via cryptography charset-normalizer==3.5.2 + # via requests click==8.4.2 + # via + # click-option-group + # semgrep + # uvicorn click-option-group==0.5.9 + # via semgrep colorama==0.4.6 + # via semgrep cryptography==50.0.2 + # via pyjwt exceptiongroup==1.2.2 + # via semgrep face==26.0.1 + # via glom glom==25.12.0 + # via semgrep googleapis-common-protos==1.75.5 + # via opentelemetry-exporter-otlp-proto-http h11==0.16.0 + # via + # httpcore + # uvicorn httpcore==1.0.9 + # via httpx httpx==0.28.1 + # via mcp httpx-sse==0.4.3 + # via mcp idna==3.20 -importlib_metadata==8.7.1 + # via + # anyio + # httpx + # requests +importlib-metadata==8.7.1 + # via opentelemetry-api jsonschema==4.25.1 + # via + # mcp + # semgrep jsonschema-specifications==2025.9.1 + # via jsonschema markdown-it-py==4.2.0 + # via rich mcp==1.29.0 + # via semgrep mdurl==0.1.2 + # via markdown-it-py opentelemetry-api==1.37.0 + # via + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-instrumentation + # opentelemetry-instrumentation-requests + # opentelemetry-instrumentation-threading + # opentelemetry-sdk + # opentelemetry-semantic-conventions + # semgrep opentelemetry-exporter-otlp-proto-common==1.37.0 + # via opentelemetry-exporter-otlp-proto-http opentelemetry-exporter-otlp-proto-http==1.37.0 + # via semgrep opentelemetry-instrumentation==0.58b0 + # via + # opentelemetry-instrumentation-requests + # opentelemetry-instrumentation-threading opentelemetry-instrumentation-requests==0.58b0 + # via semgrep opentelemetry-instrumentation-threading==0.58b0 + # via semgrep opentelemetry-proto==1.37.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-http opentelemetry-sdk==1.37.0 + # via + # opentelemetry-exporter-otlp-proto-http + # semgrep opentelemetry-semantic-conventions==0.58b0 + # via + # opentelemetry-instrumentation + # opentelemetry-instrumentation-requests + # opentelemetry-sdk opentelemetry-util-http==0.58b0 + # via opentelemetry-instrumentation-requests packaging==26.3 + # via + # opentelemetry-instrumentation + # semgrep peewee==3.19.0 + # via semgrep protobuf==6.33.6 + # via + # googleapis-common-protos + # opentelemetry-proto pycparser==3.11 + # via cffi pydantic==2.14.1 + # via + # mcp + # pydantic-settings +pydantic-core==2.50.1 + # via pydantic pydantic-settings==2.16.0 -pydantic_core==2.50.1 -Pygments==2.21.0 -PyJWT==2.15.1 + # via mcp +pygments==2.21.0 + # via rich +pyjwt==2.15.1 + # via + # mcp + # semgrep python-dotenv==1.2.4 + # via pydantic-settings python-multipart==0.0.32 + # via mcp referencing==0.37.0 + # via + # jsonschema + # jsonschema-specifications requests==2.34.2 + # via + # opentelemetry-exporter-otlp-proto-http + # semgrep rich==15.0.0 + # via semgrep rpds-py==2026.9.1 -ruamel.yaml==0.19.1 -ruamel.yaml.clib==0.2.15 + # via + # jsonschema + # referencing +ruamel-yaml==0.19.1 + # via semgrep +ruamel-yaml-clib==0.2.15 + # via semgrep semantic-version==2.10.0 + # via semgrep semgrep==1.180.0 + # via -r requirements-semgrep.in sse-starlette==3.5.0 + # via mcp starlette==1.8.0 + # via + # mcp + # sse-starlette tomli==2.4.1 + # via semgrep +typing-extensions==4.16.0 + # via + # anyio + # mcp + # opentelemetry-api + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-sdk + # opentelemetry-semantic-conventions + # pydantic + # pydantic-core + # pydantic-settings + # semgrep + # typing-inspection typing-inspection==0.4.4 -typing_extensions==4.16.0 + # via + # mcp + # pydantic + # pydantic-settings urllib3==2.8.0 + # via + # requests + # semgrep uvicorn==0.54.0 + # via mcp wcmatch==8.5.2 + # via semgrep wrapt==1.17.3 + # via + # opentelemetry-instrumentation + # opentelemetry-instrumentation-threading zipp==4.1.1 + # via importlib-metadata