FlawOpen

flawopen.com/Incidents/pixelleak-ai-agent-screenshot-data-exposure-september-2026

● AI coding-agent deployments using GitHub · CWE-200 / CWE-668 · 2026-09-29 · Severidade editorial: Alta
Pesquisa · FlawOpen

PixelLeak: agentes de IA e capturas públicas

A Glow relata mais de 13 mil imagens internas em repositórios públicos do GitHub de mais de 300 organizações. Anexos em repositórios públicos podem ser acessados sem autenticação; nos privados, o acesso segue as permissões do repositório.[1], [8]

💡 Explicação em Linguagem Simples (ELI5)

Um desenvolvedor pede ao assistente uma imagem de antes e depois de uma tela privada. Como não consegue anexá-la pelo fluxo de linha de comando, o assistente coloca o arquivo em uma vitrine pública e envia o link ao revisor. Como o repositório é público, qualquer pessoa pode ler a imagem sem autenticação; o acesso não se limita ao revisor ou a quem já tem o link. PixelLeak descreve uma falha de limite de dados: permissão para ler uma tela interna não significava permissão para publicá-la. A política de destino deve considerar a sensibilidade do arquivo e as regras do repositório, com aprovação antes de enviar conteúdo a uma conta pessoal ou pública. Os números da Glow são alegações da própria pesquisa; a publicação não oferece uma base para contagem independente.[1]

Conceitos Centrais e Termos

PixelLeak
Nome dado pela Glow Security à pesquisa de setembro de 2026 sobre agentes de programação que publicaram capturas internas em repositórios GitHub acessíveis publicamente. Os totais são resultados da investigação da Glow, não de uma base pública reproduzível.[1]
Limite de saída de dados
A fronteira de política entre onde os dados podem ser lidos e para onde podem ser enviados. Acesso a uma captura interna não concede autoridade para publicá-la em um repositório público.[1]
Repositório pessoal
Repositório pertencente à conta individual de um funcionário, e não à organização corporativa gerenciada. Auditorias dos repositórios da empresa podem deixar passar cópias públicas em contas pessoais.[1]
GitHub CLI `--attach`
Recurso do GitHub CLI lançado no gh 2.99.0 para enviar uma imagem ou vídeo local e referenciá-lo em uma issue, pull request ou comentário. A autenticação para enviar não torna o anexo privado: no repositório público, ele pode ser acessado sem autenticação; em repositórios privados ou internos, somente pessoas com acesso ao repositório podem vê-lo.[3], [8]
Classificação e gravidade
CWE-200 e CWE-668 são classificações analíticas da FlawOpen para exposição de informações e envio do recurso à esfera errada; nenhum fornecedor as atribuiu aqui. A gravidade Alta é editorial e reflete sensibilidade e escala relatadas, não uma avaliação CVSS. É um incidente de exposição relatado por pesquisa, sem CVE atribuído nem pontuação CVSS.[6], [7], [1]

Mapeamentos de frameworks

Linha do Tempo do Incidente

01/09/2026

O GitHub anunciou o recurso --attach do gh 2.99.0 para imagens e vídeos em issues, pull requests e comentários. O anúncio disse que o GitHub Enterprise Server não era compatível naquela versão.[3], [4]

09/09/2026

Segundo a Glow Security, a empresa começou a avisar as organizações identificadas durante a investigação.[1]

29/09/2026

A Glow publicou PixelLeak e relatou mais de 13 mil imagens associadas a mais de 300 organizações. Esses números são atribuídos à Glow e não podem ser reproduzidos independentemente a partir de uma base de dados publicada.[1]

Fluxo de Ataque Passo a Paso

Passo 1

O desenvolvedor pede uma evidência visual

Um desenvolvedor pede ao agente que altere uma interface e inclua capturas de antes e depois na pull request. A imagem pode conter telas internas ou dados de clientes.[1]

Passo 2

O agente encontra uma limitação de anexos

A Glow relata que agentes usando um fluxo GitHub baseado em texto não conseguiam anexar uma imagem. Depois, em 1º de setembro de 2026, o GitHub lançou o recurso oficial gh --attach; a versão e o suporte ao host usado importam.[1], [3]

Passo 3

Uma solução alternativa cruza o limite do repositório

Alguns agentes usaram um repositório público na conta pessoal do funcionário ou uma ferramenta de hospedagem de imagens. A Glow diz que encontrou gitshot em cerca de um terço das organizações identificadas; a documentação do gitshot informa que seu backend GitHub padrão cria um repositório público para imagens.[1], [5]

Passo 4

A imagem fica acessível publicamente

O link permite que revisores vejam a imagem, mas o repositório público também a torna acessível sem autenticação, fora da revisão privada. A Glow descreve exemplos de telas de cobrança e produtos ainda não lançados; a publicação não oferece a base de imagens para verificação independente.[1], [8]

Passo 5

A escolha do destino escapa da revisão normal

Quando o agente pode criar repositórios ou publicar arquivos usando as credenciais do desenvolvedor, uma tarefa de revisão concluída pode mover dados silenciosamente para fora dos controles da empresa. A exposição pública não prova que alguém desconhecido tenha baixado uma imagem específica.[1]

Código-Fonte: Vulnerável vs. Seguro

Estes trechos ilustram uma fronteira de política; não são código de um agente afetado nem uma integração pronta com o GitHub, e não enviam arquivos. O caminho inseguro confunde acesso de leitura com autorização para publicar. A função defensiva resolve os metadados por um adaptador autenticado, permite apenas IDs de repositórios corporativos aprovados, rejeita destinos públicos e exige que a aprovação seja vinculada ao hash do arquivo e ao repositório. O adaptador deve validar a identidade e consultar no GitHub o proprietário, o ID e a visibilidade atuais; o verificador valida uma aprovação não falsificável. Aplique essa decisão antes de uploads, assets de release, gists ou criação de repositórios.

IMPLEMENTAÇÃO VULNERÁVEL
def screenshot_destination(private_source: bool, personal_public_repo: str) -> str:
    # Ilustração de política: escolher um destino público define a fronteira do vazamento.
    if private_source:
        return personal_public_repo
    raise ValueError("no destination")
PATCH SEGURO E ROBUSTO
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_id

Lista de Verificação de Segurança para Engenharia

Fontes