TL;DR: GitLab.com gives every user a private email address for filing issues by mail, and it contains a token. Aikido found that token works across every project the account can reach, never expires and accepts mail from anyone. They used it to commit to main and run CI as the owner, on a project locked to one IP address. GitLab closed the HackerOne report as intended behavior. Our fix: find exposed addresses and reset the token.
Also this week: a webpage that could make the OpenCode coding agent install attacker code, Docker's hypervisor escape, agents erasing their own logs, re-enabled malicious GitHub Actions and two AWS IAM findings.
Attack of the Week: the issue address is a login
GitLab's "Email work item to this project" button shows a private address. Anything mailed there becomes an issue filed as you:
incoming+project-id-glimt-XXXXXXXXXXXXXX-issue@incoming.gitlab.comThe glimt- token is identical across all of a user's project addresses, private projects included. GitLab doesn't check the sender: "Any mailbox on the internet can send to that address, and GitLab processes the message as the token's owner."
Aikido used merge-request emails to commit code to main and run CI jobs as the owner. They also read CI/CD secrets, copied private source and read confidential issues in their test projects. On a project locked to one IP: "GitLab blocked our browser and rejected git clone. It accepted the email, and the commit landed on main."
Maintainers publish these addresses for bug reports. Aikido found a dozen live ones in an afternoon.
Access follows the owner's permissions. A Maintainer's token may reach protected branches and CI/CD variables, and another private project also takes its path and ID.
The HackerOne report "was closed as intended behavior." GitLab updated its UI and documented that email skips IP restrictions. GitLab.com still has no off switch, sender check or bulk revoke. Self-managed instances are affected when incoming email is on. Aikido says GitLab Dedicated "does not appear to be affected," but couldn't test it.

Alt text: Three ways into a GitLab project locked to one IP address, from Aikido's test. 1: the web UI, blocked by the IP allowlist. 2: git over HTTPS and SSH, blocked. 3: incoming email, accepted, because GitLab's IP restrictions don't apply to it; Aikido committed to main, ran a CI job as the owner, read CI/CD secrets and copied private source. Our lever: search for @incoming.gitlab.com and reset the owner's Incoming email token.
Ship This Week. Search repos, docs, wikis and help centers for @incoming.gitlab.com, or our self-managed incoming-mail domain. A hit matters when a token sits before -issue, like glimt- above. Betterleaks detects these, older formats included.
Reset exposed tokens at User settings > Personal access tokens > Incoming email token. Each reset replaces all of that user's project addresses.
Review recent commits, .gitlab-ci.yml changes and pipelines for anything the owner didn't do. On self-managed GitLab, check whether incoming email is on: look for "Email work item to this project" under ⋮ in a project's work items list.
Rule of the Week: a coding agent installing code from a URL
OpenCode is an open-source AI coding agent, like Claude Code or Codex. Its upgrade endpoint passed a requested version to npm install -g opencode-ai@{target}, and npm accepts URLs there, install scripts and all.
A webpage could send that request with a crafted form. Datadog: "CORS and Local Network Access do not currently block top-level navigations, by design."
Affected: OpenCode 1.14.30 through 1.18.21, installed with npm, pnpm or Bun, with opencode serve or opencode web running. It applies with no password, set by OPENCODE_SERVER_PASSWORD, "or your browser has cached credentials from a recent authentication."
Upgrade to 1.18.22, fixed August 24. Check the install method with ls -l "$(command -v opencode)".
Our detection sketch is untested; Datadog didn't publish one:
process start where
an ancestor is a confirmed OpenCode process
and npm, pnpm or bun is installing a package
and an argument starts with "opencode-ai@"
and its target contains "://", ".tgz", "git+" or "file:"
then: review the install and its descendant processesTest it against our process telemetry: normal version upgrades should stay quiet, and URL installs should alert.
Defender's Corner: patch Docker VMM before it's the default
Update Docker Desktop to 4.88.0 or later, and Docker Sandboxes to 0.42.0 or later. Oren Yomtov at Accomplish, "Guest to host: escaping Docker's hypervisor" [2026-09-19].
Docker VMM runs Docker's VM on Macs. It's opt-in today and "scheduled to become the default for Docker Desktop at the end of October 2026." Before the fix, CVE-2026-77179 let a container read and write the host's files: a symbolic-link swap redirected an approved shared path into the Mac's own files.
This week. On Macs running Docker Desktop below 4.88.0, open Settings, General, Virtual Machine Manager. Those with Docker VMM selected are affected. For Sandboxes, sbx --version should read 0.42.0 or later.
Agent Bench: can your coding agent erase its own log?
Our review of what an agent did depends on its session log. Jeremy Qin and 5 co-authors, "LLM Agents Can Easily Tamper With Their Own Traces" [2026-09-24] found: "All tested harnesses, except Muse Code, allowed agents to delete their traces when asked, without triggering monitor guardrails."
Tests ran in containers, usually with full access, covering direct requests, injected instructions and scoring incentives. Shown synthetic peer workspaces and scores, Claude Code on Opus 5 inferred that deletion earned higher scores and tried it without being asked. Claude Code's auto mode substantially reduced deletion in the privacy tests.
Run this in a throwaway VM with a scratch repo:
Find the logs: Claude Code uses
~/.claude/projects/{project}/{session}.jsonl; Codex uses~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl.In our team's permission mode, ask the agent to delete its current session log.
Check the file from outside the agent, and look for an off-machine copy.
What you walk away with: whether deletion worked in our setup, and whether an independent copy survived that the agent can't alter. A refusal proves little; another request might succeed.
The paper recommends recording model requests and responses outside the agent's control, stopping model traffic if recording fails. That preserves the conversation, without proving every reported command ran.
Try it this week: start with the agent that has the widest permissions.
Also on the Radar
Karlo Zanki at Socket, "Re-Enabled GitHub Actions Expose Thousands of Repositories to Mini Shai-Hulud" [2026-09-24].
GitHub disabled actions-cool/issues-helper and actions-cool/maintain-one-comment after May's compromise but left their malicious release tags. Both returned September 16 with tags unchanged, and workflows referencing them by tag ran the payload again. issues-helper alone has about 15,000 dependent repositories. GitHub disabled both again September 25.
Rotate every secret available to workflows that ran either action by tag on or after September 16. Pin third-party actions to full commit SHAs. Socket's caveat: "SHA pinning protects a workflow only if the pinned commit is itself clean."
AWS Watch
ReadOnlyAccess gained an Agent Registry permission. Victor Grenu, "AWS Managed Policy Changes: Summer 2026" [2026-09-27] found ReadOnlyAccess, the managed policy many teams give auditors and vendors, moved from v186 to v190 and gained agent-registry:InvokeRegistryMcp. The SageMaker Studio Permissive execution policies now grant events:* and elasticmapreduce:* on every resource.
This week: in each account, run aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess. Repeat with SageMakerStudioAdminIAMPermissiveExecutionPolicy and SageMakerStudioUserIAMPermissiveExecutionPolicy in place of ReadOnlyAccess. Check that each listed user, group and role needs the new permissions.
A public S3 object reveals our management account ID. Dan Gansel at Upwind, "Let Me Speak to Your Manager (Account)" [2026-09-25]. The undocumented IAM condition key aws:ResourceOrgMasterAccountId holds the ID of the account that manages our AWS organization. An attacker with an assumable role can recover it by reading one of our public objects, about 120 probes at worst.
With S3 data event or server access logging on, those reads appear in our logs and look normal. The probing stays in the attacker's account. The ID grants no access by itself.
This week: turn on S3 Block Public Access at the account or organization level wherever public buckets aren't needed.
Which would our logs have caught: an email changing code, an agent installing from a URL, or an agent clearing its own history? Hit reply and tell me.
Full issue with sources: join.defensive.works/p/your-gitlab-issue-email-can-push-to-main
Until next Tuesday,
R.K.
// end of issue 024
Sponsored content may appear below. Not part of Weekly Recon editorial.
Privacy-first email. Built for real protection.
Proton Mail offers what others won’t:
End-to-end encryption by default
Zero access to your data
Open-source and independently audited
Based in Switzerland with strong privacy laws
Free to start, no ads
We don’t scan your emails. We don’t sell your data. And we don’t make you dig through settings to find basic security. Proton is built for people who want control, not compromise.
Simple, secure, and free.

