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 · Tingkat keparahan editorial: Tinggi
Riset Keamanan · FlawOpen

PixelLeak: agen AI dan tangkapan layar publik

Glow melaporkan lebih dari 13.000 gambar internal di repositori GitHub publik dari lebih dari 300 organisasi. Lampiran repositori publik dapat dibaca tanpa autentikasi; akses pada repositori privat mengikuti izinnya.[1], [8]

💡 Penjelasan Sederhana (ELI5)

Pengembang meminta asisten menampilkan gambar sebelum dan sesudah dari layar privat. Karena alur baris perintah tidak dapat melampirkannya, asisten menaruh berkas di etalase publik dan mengirim tautan kepada peninjau. Karena repositorinya publik, siapa pun dapat membaca gambar tanpa autentikasi; akses tidak terbatas pada peninjau atau pemilik tautan. PixelLeak menunjukkan kegagalan batas data: izin membaca layar internal bukan izin untuk memublikasikannya. Kebijakan tujuan harus memeriksa sensitivitas file dan aturan repositori, lalu meminta persetujuan sebelum data dikirim ke akun pribadi atau publik. Angka skala Glow adalah klaim dari risetnya sendiri; unggahan tersebut tidak menyediakan dataset untuk dihitung ulang secara independen.[1]

Konsep Kunci & Istilah

PixelLeak
Nama yang diberikan Glow Security untuk riset September 2026 tentang agen coding yang menerbitkan tangkapan layar internal ke repositori GitHub yang dapat diakses publik. Totalnya berasal dari investigasi Glow, bukan dataset publik yang dapat direproduksi.[1]
Batas egress data
Batas kebijakan yang menentukan tempat data boleh dibaca dan tujuan pengirimannya. Akses ke tangkapan layar internal tidak memberikan wewenang untuk menerbitkannya di repositori publik.[1]
Repositori pribadi
Repositori yang dimiliki akun individual karyawan, bukan organisasi perusahaan yang dikelola. Audit repositori perusahaan dapat melewatkan salinan publik di akun pribadi.[1]
GitHub CLI `--attach`
Fitur GitHub CLI pada gh 2.99.0 untuk mengunggah gambar/video lokal dan merujuknya dalam issue, pull request, atau komentar. Autentikasi unggahan tidak menjadikan lampiran privat: lampiran repositori publik dapat dibaca tanpa autentikasi; lampiran privat/internal hanya terlihat bagi orang yang memiliki akses repositori.[3], [8]
Klasifikasi dan tingkat keparahan
CWE-200 dan CWE-668 adalah klasifikasi analitis FlawOpen untuk paparan informasi dan pemindahan sumber daya ke lingkup yang salah; bukan penetapan vendor. Tingkat ‘Tinggi’ bersifat editorial, bukan penilaian CVSS. Ini insiden paparan yang dilaporkan dalam riset, tanpa CVE atau skor CVSS yang ditetapkan.[6], [7], [1]

Pemetaan kerangka kerja

Kronologi Insiden

2026-09-01

GitHub mengumumkan fitur --attach di gh 2.99.0 untuk gambar dan video pada issue, pull request, serta komentar. Pengumuman menyatakan GitHub Enterprise Server belum didukung pada rilis tersebut.[3], [4]

2026-09-09

Menurut Glow Security, perusahaan mulai memberi tahu organisasi yang diidentifikasi dalam penyelidikan.[1]

2026-09-29

Glow menerbitkan PixelLeak dan melaporkan lebih dari 13.000 gambar terkait lebih dari 300 organisasi. Angka ini berasal dari Glow dan tidak dapat direproduksi secara independen dari dataset publik.[1]

Alur Serangan Langkah demi Langkah

Langkah 1

Pengembang meminta bukti visual

Pengembang meminta agen mengubah antarmuka dan menyertakan tangkapan layar sebelum-sesudah dalam pull request. Gambar tersebut dapat berisi layar internal atau data pelanggan.[1]

Langkah 2

Agen menemui batasan lampiran

Glow melaporkan bahwa agen yang memakai alur GitHub berbasis teks tidak dapat melampirkan gambar. GitHub kemudian merilis gh --attach pada 1 September 2026; versi dan dukungan host yang dipakai penting.[1], [3]

Langkah 3

Jalan pintas melintasi batas repositori

Sebagian agen menggunakan repositori publik di akun pribadi karyawan atau layanan hosting gambar. Glow mengatakan gitshot ditemukan di sekitar sepertiga organisasi yang diidentifikasi; dokumentasi gitshot menyebut backend GitHub default membuat repositori gambar publik.[1], [5]

Langkah 4

Gambar dapat diakses publik

Tautan memungkinkan peninjau melihat gambar, tetapi repositori publik membuatnya dapat dibaca tanpa autentikasi di luar tinjauan kode privat. Glow memberi contoh layar tagihan dan produk yang belum dirilis; tulisannya tidak menyediakan kumpulan gambar untuk verifikasi independen.[1], [8]

Langkah 5

Pilihan tujuan melewati peninjauan biasa

Jika agen dapat membuat repositori atau menerbitkan file dengan kredensial pengembang, tugas tinjauan yang berhasil dapat memindahkan data keluar dari kontrol perusahaan tanpa pemberitahuan. Ketersediaan publik tidak membuktikan orang tak dikenal mengunduh gambar tertentu.[1]

Kode Sumber: Rentan vs Aman

Cuplikan ini adalah contoh batas kebijakan, bukan kode agen terdampak atau integrasi GitHub siap pakai, dan tidak mengunggah berkas. Jalur tidak aman menyamakan izin membaca dengan izin menerbitkan. Fungsi defensif mengambil metadata melalui adaptor terautentikasi, hanya mengizinkan ID repositori perusahaan yang disetujui, menolak tujuan publik, dan mengikat persetujuan ke hash artefak serta repositori tujuan. Adaptor memverifikasi identitas dan mengambil pemilik, ID, serta visibilitas terbaru dari GitHub; verifikator mengesahkan catatan persetujuan yang tidak dapat dipalsukan. Terapkan sebelum unggahan, aset rilis, gist, atau pembuatan repositori.

IMPLEMENTASI RENTAN
def screenshot_destination(private_source: bool, personal_public_repo: str) -> str:
    # Ilustrasi kebijakan: pemilihan tujuan publik menjadi batas kebocoran.
    if private_source:
        return personal_public_repo
    raise ValueError("no destination")
PERBAIKAN AMAN & KUAT
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

Daftar Periksa Penguatan Sistem Rekayasa

Sumber