flawopen.com/Incidents/pixelleak-ai-agent-screenshot-data-exposure-september-2026
PixelLeak: AI Agents Put Screenshots on GitHub
Glow reports 13,000+ internal images in public GitHub repositories across 300+ organizations. GitHub attachments in public repositories are readable without authentication; private-repository access follows repository permissions.[1], [5]
A developer asks an assistant to show a before-and-after picture of a private screen. The assistant cannot attach the picture through its command-line route, so it puts the file in a public display case and gives the reviewer a link. Because the repository is public, the image is readable without authentication; access is not limited to the reviewer or to people who already have the link. PixelLeak is a reported data-boundary failure: permission to read an internal screen did not mean permission to publish it. Decide the destination using the artifact's sensitivity and the repository owner's policy, then require approval before data crosses into a personal or public account. Glow's scale figures are its own research claims; its report does not provide a dataset for independent counting.[1]
Core Concepts & Subsystem Terms
PixelLeak- Glow Security's name for its September 2026 research into AI coding agents publishing internal screenshots in publicly reachable GitHub repositories. The reported totals come from Glow's investigation, not a public reproducible dataset.[1]
Data egress boundary- The policy boundary between where data is allowed to be read and where it is allowed to be sent. Access to an internal screenshot does not grant authority to publish it to a public repository.[1]
Personal repository- A repository owned by an employee's individual account rather than the company's managed organization. Company-wide repository audits can miss public copies under personal accounts.[1]
GitHub CLI `--attach`- A GitHub CLI feature released in
gh2.99.0 that uploads a local image or video and references it in an issue, pull request, or comment. Upload permission does not make an attachment private: GitHub says files in public repositories are accessible without authentication, while files in private or internal repositories are visible to people with repository access.[3], [5] Classification and severityCWE-200andCWE-668are FlawOpen's analytical classifications for information exposure and placing a resource in the wrong sphere; no vendor assigned them here. High is an editorial rating reflecting sensitivity and reported scale, not a CVSS assessment. This is a research-reported exposure incident, not an assigned CVE; no CVE or CVSS score is available.[7], [8], [1]
Framework mappings
- FlawOpen’s conceptual mapping to MITRE CWE; no vendor or CVE record assigned this weakness ID to this research demonstration. CWE-200 CWE-668
- OWASP GenAI 2025 — LLM06:2025 Excessive Agency
Incident Timeline
Glow Security says it began notifying organizations identified during its investigation.[1]
Glow published PixelLeak, reporting over 13,000 images associated with more than 300 organizations. These figures are attributed to Glow and cannot be independently reproduced from a dataset in the post.[1]
Step-by-Step Attack Flow
A developer requests visual proof
A developer asks an agent to make a user-interface change and include before-and-after screenshots in a pull request. The screenshot may contain internal screens or customer data.[1]
A workaround crosses the repository boundary
Some agents used a public repository under an employee's personal account or an image-hosting tool. Glow says its investigation found gitshot in about one third of the organizations it identified; gitshot documents that its default GitHub backend creates a public image repository.[1], [6]
The image becomes publicly reachable
A link lets reviewers see the image, but the public repository makes it readable without authentication beyond the private code review. Glow describes examples involving billing screens and unreleased product work; its post does not provide the image dataset for independent verification.[1], [5]
The destination decision escapes normal review
When the agent can create repositories or publish assets with the developer's credentials, a successful code-review task can silently move data outside company-managed controls. Public exposure does not prove that an unknown party downloaded a particular image.[1]
Source Code: Flaw vs. Secure Implementation
These snippets model a policy boundary; they are not source from an affected agent or a drop-in GitHub integration, and neither uploads files. The unsafe path treats read access as permission to publish. The defensive function resolves repository metadata through an authenticated adapter, allowlists company repository IDs, rejects public destinations, and requires a verifier to bind approval to both the artifact digest and destination repository. The adapter must verify the authenticated actor and fetch current owner, repository ID, and visibility from GitHub; the verifier must validate an unforgeable approval record. Enforce this before uploads, release assets, gists, or repository creation.
def screenshot_destination(private_source: bool, personal_public_repo: str) -> str:
# Policy illustration only: choosing a public destination is the leak boundary.
if private_source:
return personal_public_repo
raise ValueError("no destination")from dataclasses import dataclass
from typing import Literal, Optional, Protocol
@dataclass(frozen=True)
class Artifact:
sha256: str
classification: Literal["public", "internal", "confidential"]
@dataclass(frozen=True)
class Repository:
repository_id: int
owner_id: int
visibility: Literal["public", "private", "internal"]
@dataclass(frozen=True)
class Approval:
artifact_sha256: str
repository_id: int
class RepositoryResolver(Protocol):
def resolve_for_authenticated_actor(self, full_name: str) -> Repository: ...
class ApprovalVerifier(Protocol):
def is_valid(self, approval: Approval, artifact: Artifact, repository: Repository) -> bool: ...
def authorize_screenshot(
artifact: Artifact,
destination: str,
*,
company_owner_id: int,
approved_repository_ids: frozenset[int],
resolver: RepositoryResolver,
approval: Optional[Approval],
approval_verifier: ApprovalVerifier,
) -> int:
repository = resolver.resolve_for_authenticated_actor(destination)
if repository.owner_id != company_owner_id or repository.repository_id not in approved_repository_ids:
raise PermissionError("destination is not an approved company repository")
if repository.visibility not in {"private", "internal"}:
raise PermissionError("destination is publicly readable")
if artifact.classification != "public":
if approval is None or not approval_verifier.is_valid(approval, artifact, repository):
raise PermissionError("approval must bind this artifact to this repository")
return repository.repository_idEngineering & System Hardening Checklist
- ✓Default-deny agent actions that create public repositories, gists, release assets, or destinations outside the company's managed organization.
- ✓Enforce destination policy at runtime using the authenticated account owner and repository visibility; do not rely on the agent's natural-language plan or a screenshot scanner alone.
- ✓Treat screenshots and recordings from internal applications as internal data by default. Require explicit reviewer approval before publishing them, even when the source code change is approved.
- ✓Audit public repositories, releases, and gists owned by employees who contribute to private repositories, including departed employees and personal accounts where policy permits review.
- ✓Inspect shared agent skills, hooks, CLI extensions, and packages for public-by-default image backends such as the documented
gitshotdefault; pin and review approved versions.[6] - ✓On GitHub.com,
gh --attachcan place media in a review context, but use it only with the intended private or internal repository: public-repository attachments are readable without authentication, while private/internal attachments follow repository access. Verify the target repository, authenticated identity, permissions, and current host support before rollout.[5], [3] - ✓If an image was exposed, remove public copies and release assets where possible, identify every mirror, revoke credentials visible in the image, and treat public availability as exposure even when access logs are unavailable.[1]
Sources
- Glow Security — PixelLeak: How AI Agents Exposed Developer Screenshots from Leading Tech Companies
- The Register — AI models keep posting screenshots showing sensitive data from inside tech companies
- GitHub Changelog — GitHub CLI: Media in issues, pull requests, and comments
- GitHub CLI 2.99.0 release notes
- GitHub Docs — Attaching files
- gitshot README — image hosting backends and public-by-default notice
- CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
- CWE-668: Exposure of Resource to Wrong Sphere