Pinned Tweet
Solo dev shipping small tools. The first is Pin Check, a free checker for GitHub Actions that aren't pinned to a commit.
Every day I post one AI or dev story with the source linked. What each app earns goes here too.
an agent instruction file planted in a customer's downloads folder told codex to install an npm package, function-color. it got blocked.
in forever security's tests codex obeyed it 9+ times in 10. a web page can drop one there with no prompt.
don't run agents from downloads.
Wiz has been tracking a campaign called Gheist since Sept 15. Attackers use stolen GitHub tokens to push a branch named ci-sync- plus 6 random characters. Its workflow prints the repo's secrets into the Actions log in base32, so masking never sees the real string. Then it pulls the log and deletes the run.
Wiz has found 13500 of these branches from more than 850 accounts. Public repos got held for approval. Private repos and GitHub Enterprise Server ran it.
Deleting the branch undoes nothing. If you find a ci-sync- branch, rotate every secret that repo had.
5 dev-security stories from this week, and what to do about each:
- railway env var breach
- litellm 1.107.0 on pypi
- node2shell in brotli
- php2shell
- github turning off pull_request_target on nov 2
one post each 🧵
php2shell lives in the php runtime. updating wordpress or laravel won't fix it. the fixed runtimes are 8.2.35, 8.3.36, 8.4.27 and 8.5.12. distro packages often patch without changing the version, so check the changelog for the CVE ids.
i put a permissions block with contents: read into every new repo's workflows before anything else.
what's the first thing you add?
the node2shell bug from this week came out of openai's red team. their own agents found it while attacking openai's training sandboxes on purpose.
several of the other ways out were ordinary infra mistakes: DNS and firewall rules that let blocked requests through, a firewall-approved connection reused to reach a blocked endpoint, and internal services that fetched whatever URL the model gave them.
the cpu cache side channel will get the headlines. if you sandbox agents, check your URL-fetching services first
if you rotated your railway secrets this week and skipped the sealed ones, go back and rotate those too.
railway support said in a reply on oct 24 that sealing only hides a variable in the UI and API. sealed values are stored encrypted the same way as the rest, so they were as reachable as everything else.
nov 2 is nine days out. that's the day github turns off pull_request_target by default in public repos.
76 of the 200 most-starred repos still use it, including react, next.js, django and pytorch. whether they lose it depends on org event policies you can't see from the repo.
if yours uses it, check your settings before the 2nd.
the name says node, but node2shell is a bug in google's brotli decoder (CVE-2026-111497). bun and the python brotli packages ship the same code.
fixed in:
node 22.23.4 / 24.22.1 / 26.12.1
bun 1.4.4
python Brotli 1.2.1, brotlicffi 1.2.1.0
node lists no workaround. if your server decompresses brotli that someone else sent, patch first and argue about severity later
litellm 1.107.0 sat on PyPI for under two hours on Oct 21. Wiz estimates about 49k downloads.
the wheels drop a .pth file into site-packages, so the stealer runs every time python starts. nobody had to import litellm.
the GitHub tag was clean. only the built package was bad, so reading the source told you nothing.
if a box pulled it, rotate every key it could see and rebuild the box
GitHub Actions has a lockfile now. I ran it on a test repo, then tampered with it.
It pins the actions inside composite actions, which a SHA pin in your YAML never reaches. v0.1.6 missed my edited lockfile. v0.2.0 catches it.
same piece on medium, if that's where you read: medium.com/@miles_builds/ac6…
github actions has a lockfile now. it also pins the actions hidden inside composite actions, which a SHA pin in your yaml never reaches.
i tampered with a lockfile on purpose. the first version i tried said it was fine. the update three days later caught it.
the full test is in the reply.
same piece on medium, if that's where you read: medium.com/@miles_builds/ac6…
railway says someone read encrypted environment variables, and for some customers got the decrypted values.
that's two platforms in a week where a key stored with a vendor got out. opencode last week, railway now.
rotate, then give each platform its own key with a budget on it.
if a branch has the same name as a commit hash, git checkout on that hash picks the branch. git prints a warning that the name is ambiguous, then checks out the branch anyway.
anything that pins to a SHA and then runs a plain git checkout can land on code nobody reviewed. run git rev-parse HEAD after the checkout and compare it to the pin.








