<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet href="/static/style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://vulnerability.circl.lu/sightings/feed</id>
  <title>Most recent sightings.</title>
  <updated>2026-08-30T23:22:39.400086+00:00</updated>
  <author>
    <name>Vulnerability-Lookup</name>
    <email>info@circl.lu</email>
  </author>
  <link href="https://vulnerability.circl.lu" rel="alternate"/>
  <generator uri="https://lkiesow.github.io/python-feedgen" version="1.0.0">python-feedgen</generator>
  <subtitle>Contains only the most 10 recent sightings.</subtitle>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/a77b58af-e79a-4481-9921-6c0c5dafeb82/export</id>
    <title>a77b58af-e79a-4481-9921-6c0c5dafeb82</title>
    <updated>2026-08-30T23:22:39.415390+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cve.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "a77b58af-e79a-4481-9921-6c0c5dafeb82", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "GHSA-XR9X-R78C-5HRM", "type": "published-proof-of-concept", "source": "https://t.me/bdufstecru/3355", "content": "\u0423\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 libvips \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 Active Storage \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b Ruby on Rails \u0441\u0432\u044f\u0437\u0430\u043d\u0430 \u0441 \u043d\u0435\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0439 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0440\u0435\u0441\u0443\u0440\u0441\u0430. \u042d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0430\u0446\u0438\u044f \u0443\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u043d\u0430\u0440\u0443\u0448\u0438\u0442\u0435\u043b\u044e, \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u043c\u0443 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0435\u0441\u0430\u043d\u043a\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0437\u0430\u0449\u0438\u0449\u0430\u0435\u043c\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438\n\nBDU:2026-10713\nCVE-2026-66066\n\n\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044f:\nhttps://discuss.rubyonrails.org/t/cve-2026-66066-possible-arbitrary-file-read-and-remote-code-execution-in-active-storage-variant-processing/91432\nhttps://github.com/rails/rails/security/advisories/GHSA-xr9x-r78c-5hrm", "creation_timestamp": "2026-08-01T00:00:29.845455Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/a77b58af-e79a-4481-9921-6c0c5dafeb82/export"/>
    <published>2026-08-01T00:00:29.845455+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/018d05e1-5d09-4694-a808-d821b58a3f2e/export</id>
    <title>018d05e1-5d09-4694-a808-d821b58a3f2e</title>
    <updated>2026-08-30T23:22:39.416915+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cve.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "018d05e1-5d09-4694-a808-d821b58a3f2e", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "GHSA-XR9X-R78C-5HRM", "type": "seen", "source": "https://t.me/bdufstecru/3355", "content": "\u0423\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 libvips \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 Active Storage \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b Ruby on Rails \u0441\u0432\u044f\u0437\u0430\u043d\u0430 \u0441 \u043d\u0435\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0439 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0440\u0435\u0441\u0443\u0440\u0441\u0430. \u042d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0430\u0446\u0438\u044f \u0443\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u043d\u0430\u0440\u0443\u0448\u0438\u0442\u0435\u043b\u044e, \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u043c\u0443 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0435\u0441\u0430\u043d\u043a\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0437\u0430\u0449\u0438\u0449\u0430\u0435\u043c\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438\n\nBDU:2026-10713\nCVE-2026-66066\n\n\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0439 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044f:\nhttps://discuss.rubyonrails.org/t/cve-2026-66066-possible-arbitrary-file-read-and-remote-code-execution-in-active-storage-variant-processing/91432\nhttps://github.com/rails/rails/security/advisories/GHSA-xr9x-r78c-5hrm", "creation_timestamp": "2026-07-31T14:00:04.423246Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/018d05e1-5d09-4694-a808-d821b58a3f2e/export"/>
    <published>2026-07-31T14:00:04.423246+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/d4e3d0cc-dc55-4caa-851e-8fe84e5b8d68/export</id>
    <title>d4e3d0cc-dc55-4caa-851e-8fe84e5b8d68</title>
    <updated>2026-08-30T23:22:39.417059+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cve.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "d4e3d0cc-dc55-4caa-851e-8fe84e5b8d68", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "GHSA-xr9x-r78c-5hrm", "type": "seen", "source": "https://gist.github.com/alon710/91befcf1d9482b0b57392c974c405ba5", "content": "# CVE-2026-66066: CVE-2026-66066: Pre-Authentication Arbitrary File Read and Remote Code Execution in Ruby on Rails Active Storage\n\n&amp;gt; **CVSS Score:** 9.8\n&amp;gt; **Published:** 2026-07-30\n&amp;gt; **Full Report:** https://cvereports.com/reports/CVE-2026-66066\n\n## Summary\nCVE-2026-66066 (popularly known as 'KindaRails2Shell') is a critical security vulnerability in the Active Storage component of Ruby on Rails. The vulnerability arises from an insecure default integration with the libvips image processing library via the ruby-vips gem. Under default configurations, Active Storage fails to restrict untrusted format loaders within libvips, allowing remote, unauthenticated attackers to upload malformed files that leverage external dataset features to read local server files. By extracting cryptographic secrets such as SECRET_KEY_BASE from the leaked file contents, attackers can forge signed Marshal serialization payloads to achieve remote code execution.\n\n## TL;DR\nActive Storage using libvips allows unauthenticated remote attackers to read arbitrary server files by uploading a crafted MATLAB/HDF5 file, extract SECRET_KEY_BASE, and execute arbitrary commands via forged Marshal deserialization payloads.\n\n## Exploit Status: POC\n\n## Technical Details\n\n- **CWE ID**: CWE-1188 / CWE-502\n- **Attack Vector**: Network (Unauthenticated)\n- **CVSS v3.1 Score**: 9.8\n- **CVSS v4.0 Score**: 9.5\n- **Exploit Status**: Proof of Concept (PoC) Available\n- **KEV Status**: Not Listed\n\n## Affected Systems\n\n- Ruby on Rails (Active Storage / Action Pack) using libvips\n- **Ruby on Rails (Active Storage)**: &amp;lt; 7.2.3.2 (Fixed in: `7.2.3.2`)\n- **Ruby on Rails (Active Storage)**: &amp;gt;= 8.0.0.beta1, &amp;lt; 8.0.5.1 (Fixed in: `8.0.5.1`)\n- **Ruby on Rails (Active Storage)**: &amp;gt;= 8.1.0.beta1, &amp;lt; 8.1.3.1 (Fixed in: `8.1.3.1`)\n\n## Mitigation\n\n- Upgrade the Ruby on Rails framework to patched release versions.\n- Verify and upgrade system-level dependencies for libvips and ruby-vips.\n- Switch the Active Storage variant processor to mini_magick as a temporary workaround.\n- Configure restrictive security policies on host image-processing utilities.\n\n**Remediation Steps:**\n1. Identify the current active variant processor in config/environments/production.rb.\n2. Update Rails Gemfile declarations to target versions 7.2.3.2, 8.0.5.1, or 8.1.3.1.\n3. Ensure host package managers are updated to install libvips version 8.13 or newer.\n4. Update the ruby-vips gem dependency to version 2.2.1 or newer.\n5. Deploy the updated application and restart system-level processes to load the new library-level blocks.\n\n## References\n\n- [GitHub Security Advisory (Rails Core)](https://github.com/rails/rails/security/advisories/GHSA-xr9x-r78c-5hrm)\n- [Official Fix Commit - 1c01bb58](https://github.com/rails/rails/commit/1c01bb587206ee6eb0e1179c2cef96a6a47acb1e)\n- [Official Fix Commit - 349e7a5d](https://github.com/rails/rails/commit/349e7a5d5b4b715af1e416db824f3c078a7d59e5)\n- [Official Fix Commit - d79b7f4a](https://github.com/rails/rails/commit/d79b7f4aa17dec8ce4960fef05733c8c0c7ef49a)\n- [Rubysec Advisory Entry](https://github.com/rubysec/ruby-advisory-db/blob/master/gems/activestorage/CVE-2026-66066.yml)\n- [Official Rails Release v7.2.3.2](https://github.com/rails/rails/releases/tag/v7.2.3.2)\n- [Official Rails Release v8.0.5.1](https://github.com/rails/rails/releases/tag/v8.0.5.1)\n- [Official Rails Release v8.1.3.1](https://github.com/rails/rails/releases/tag/v8.1.3.1)\n- [Industry Disclosure](https://thehackernews.com/2026/07/critical-rails-flaw-could-let.html)\n\n\n---\n*Generated by [CVEReports](https://cvereports.com/reports/CVE-2026-66066) - Automated Vulnerability Intelligence*", "creation_timestamp": "2026-07-30T19:31:45.005662Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/d4e3d0cc-dc55-4caa-851e-8fe84e5b8d68/export"/>
    <published>2026-07-30T19:31:45.005662+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/812811ac-0c15-4578-a3cd-af4b96bdcda8/export</id>
    <title>812811ac-0c15-4578-a3cd-af4b96bdcda8</title>
    <updated>2026-08-30T23:22:39.417207+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cve.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "812811ac-0c15-4578-a3cd-af4b96bdcda8", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "GHSA-xr9x-r78c-5hrm", "type": "seen", "source": "https://bsky.app/profile/pmorinerie.mastodon.xyz.ap.brid.gy/post/3mrustbzfclq2", "content": "Oups, cette vuln\u00e9rabilit\u00e9 Rails a l'air pas glop. Si vous utilisez ActiveStorage + vips, y'a des choses \u00e0 faire (workaround ou mise \u00e0 jour).\n\nhttps://github.com/rails/rails/security/advisories/GHSA-xr9x-r78c-5hrm\n\n#Rails #Ruby #VIPS #CVE", "creation_timestamp": "2026-07-30T16:02:30.390311Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/812811ac-0c15-4578-a3cd-af4b96bdcda8/export"/>
    <published>2026-07-30T16:02:30.390311+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/c4e5e0c1-a918-43c6-ad6f-c5b4e2724842/export</id>
    <title>c4e5e0c1-a918-43c6-ad6f-c5b4e2724842</title>
    <updated>2026-08-30T23:22:39.417316+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cve.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "c4e5e0c1-a918-43c6-ad6f-c5b4e2724842", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "GHSA-xr9x-r78c-5hrm", "type": "seen", "source": "https://gist.github.com/cskartikey/e1db6fa823980f7d870432de59e29462", "content": "#!/usr/bin/env python3\n\"\"\"Fast Cloudflare R2 scan for CVE-2026-66066 / GHSA-xr9x-r78c-5hrm uploads.\n\nExample: scan_r2_cve2026_66066.py --workers 100 --jsonl r2-hits.jsonl\nCredentials via environment:\n\n    export R2_ACCOUNT_ID=...\n    export R2_ACCESS_KEY_ID=...\n    export R2_SECRET_ACCESS_KEY=...\n    export R2_BUCKET=...\n\n    python3 -m pip install boto3\n    python3 scan_r2_cve2026_66066.py\n\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport json\nimport os\nimport sys\nimport threading\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\nfrom dataclasses import asdict, dataclass\n\ntry:\n    import boto3\n    from botocore.client import Config\n    from botocore.exceptions import ClientError\nexcept ModuleNotFoundError:\n    print(\"error: install boto3: python3 -m pip install boto3\", file=sys.stderr)\n    raise SystemExit(2)\n\n\nMATLAB_PREFIX = b\"MATLAB 5.0\"\nHDF5_SIGNATURE = b\"\\x89HDF\\r\\n\\x1a\\n\"\nHDF5_OFFSET = 512\nPOC_TRAILER_MAGIC = b\"RAILS_GHSA_OAST_PAYLOAD_V1\"\nBMP_MAGIC = b\"BM\"\nTHREAD_LOCAL = threading.local()\n\n\n@dataclass\nclass Hit:\n    key: str\n    size: int\n    etag: str\n    reasons: list[str]\n    head_hex: str\n    content_type: str = \"\"\n    tail_hex: str = \"\"\n\n\ndef parse_args() -&amp;gt; argparse.Namespace:\n    parser = argparse.ArgumentParser(\n        description=(\n            \"Parallel R2 byte-sample scan for MATLAB/HDF5 Active Storage \"\n            \"exploit uploads (CVE-2026-66066).\"\n        )\n    )\n    parser.add_argument(\n        \"--bucket\",\n        default=os.environ.get(\"R2_BUCKET\"),\n        help=\"R2 bucket name (or set R2_BUCKET)\",\n    )\n    parser.add_argument(\n        \"--prefix\",\n        default=os.environ.get(\"R2_PREFIX\", \"\"),\n        help=\"optional key prefix (Active Storage root prefix, if any)\",\n    )\n    parser.add_argument(\n        \"--endpoint\",\n        default=os.environ.get(\"R2_ENDPOINT\"),\n        help=\"R2 S3 API endpoint (default derived from R2_ACCOUNT_ID)\",\n    )\n    parser.add_argument(\n        \"--head-bytes\",\n        type=int,\n        default=768,\n        help=\"bytes to fetch from object start (default: %(default)s)\",\n    )\n    parser.add_argument(\n        \"--tail-bytes\",\n        type=int,\n        default=1024,\n        help=\"bytes to fetch from object end only on head hits (default: %(default)s)\",\n    )\n    parser.add_argument(\n        \"--min-size\",\n        type=int,\n        default=1024,\n        help=(\n            \"skip objects smaller than this (default: %(default)s; \"\n            \"lab artifact is ~2-4 KiB)\"\n        ),\n    )\n    parser.add_argument(\n        \"--max-size\",\n        type=int,\n        default=256 * 1024,\n        help=(\n            \"skip objects larger than this (default: 256KiB). Observed prod \"\n            \"MATLAB probes were ~74KiB declared as image/png.\"\n        ),\n    )\n    parser.add_argument(\n        \"--workers\",\n        type=int,\n        default=32,\n        help=\"parallel Range-GET workers (default: %(default)s)\",\n    )\n    parser.add_argument(\n        \"--max-objects\",\n        type=int,\n        default=0,\n        help=\"stop after sampling N objects (0 = no limit)\",\n    )\n    parser.add_argument(\n        \"--jsonl\",\n        type=str,\n        default=\"\",\n        help=\"optional path to write hit records as JSONL\",\n    )\n    return parser.parse_args()\n\n\ndef require_env(name: str) -&amp;gt; str:\n    value = os.environ.get(name, \"\").strip()\n    if not value:\n        raise SystemExit(f\"error: set {name} in the environment\")\n    return value\n\n\ndef make_client(endpoint: str | None):\n    account_id = os.environ.get(\"R2_ACCOUNT_ID\", \"\").strip()\n    access_key = require_env(\"R2_ACCESS_KEY_ID\")\n    secret_key = require_env(\"R2_SECRET_ACCESS_KEY\")\n    if not endpoint:\n        if not account_id:\n            raise SystemExit(\n                \"error: set R2_ENDPOINT or R2_ACCOUNT_ID in the environment\"\n            )\n        endpoint = f\"https://{account_id}.r2.cloudflarestorage.com\"\n    return boto3.client(\n        \"s3\",\n        endpoint_url=endpoint,\n        aws_access_key_id=access_key,\n        aws_secret_access_key=secret_key,\n        region_name=os.environ.get(\"AWS_REGION\", \"auto\"),\n        config=Config(\n            signature_version=\"s3v4\",\n            max_pool_connections=64,\n            retries={\"max_attempts\": 3, \"mode\": \"standard\"},\n        ),\n    )\n\n\ndef client_for_thread(endpoint: str | None):\n    client = getattr(THREAD_LOCAL, \"client\", None)\n    if client is None:\n        client = make_client(endpoint)\n        THREAD_LOCAL.client = client\n    return client\n\n\ndef get_range(client, bucket: str, key: str, start: int, end: int) -&amp;gt; bytes:\n    response = client.get_object(\n        Bucket=bucket,\n        Key=key,\n        Range=f\"bytes={start}-{end}\",\n    )\n    return response[\"Body\"].read()\n\n\ndef analyze_head(key: str, size: int, etag: str, head: bytes) -&amp;gt; Hit | None:\n    reasons: list[str] = []\n\n    if head.startswith(MATLAB_PREFIX):\n        reasons.append(\"matlab_5_header\")\n    if len(head) &amp;gt;= HDF5_OFFSET + len(HDF5_SIGNATURE):\n        if head[HDF5_OFFSET : HDF5_OFFSET + len(HDF5_SIGNATURE)] == HDF5_SIGNATURE:\n            reasons.append(\"hdf5_sig_at_512\")\n    if POC_TRAILER_MAGIC in head:\n        reasons.append(\"poc_trailer_magic\")\n    for marker in (b\"MATLAB_class\", b\"/proc/\", b\"SECRET_KEY_BASE\"):\n        if marker in head:\n            reasons.append(f\"marker:{marker.decode('ascii', 'replace')}\")\n\n    # Declared BMP is checked only if we later learn content-type; for raw\n    # bytes, a non-BM object that still looks like MAT/HDF5 is enough.\n    if \"matlab_5_header\" in reasons and \"hdf5_sig_at_512\" in reasons:\n        reasons.append(\"mat_hdf5_hybrid_layout\")\n\n    if not reasons:\n        return None\n\n    return Hit(\n        key=key,\n        size=size,\n        etag=etag.strip('\"'),\n        reasons=sorted(set(reasons)),\n        head_hex=head[:64].hex(),\n    )\n\n\ndef enrich_tail(\n    client,\n    bucket: str,\n    hit: Hit,\n    size: int,\n    head: bytes,\n    tail_bytes: int,\n) -&amp;gt; Hit:\n    if tail_bytes &amp;lt;= 0:\n        return hit\n    if size &amp;lt;= len(head):\n        tail = head\n    else:\n        start = max(0, size - tail_bytes)\n        try:\n            tail = get_range(client, bucket, hit.key, start, size - 1)\n        except ClientError as error:\n            print(f\"warn: tail get failed key={hit.key}: {error}\", file=sys.stderr)\n            return hit\n\n    extra: list[str] = []\n    if POC_TRAILER_MAGIC in tail:\n        extra.append(\"poc_trailer_magic\")\n    for marker in (b\"MATLAB_class\", b\"/proc/\", b\"SECRET_KEY_BASE\", b\"rails_ghsa\"):\n        if marker in tail:\n            extra.append(f\"marker:{marker.decode('ascii', 'replace')}\")\n    if extra:\n        hit.reasons = sorted(set(hit.reasons) | set(extra))\n    hit.tail_hex = tail[-64:].hex()\n    return hit\n\n\ndef iter_candidates(client, bucket: str, prefix: str, min_size: int, max_size: int):\n    token = None\n    listed = 0\n    kept = 0\n    while True:\n        kwargs = {\"Bucket\": bucket, \"Prefix\": prefix, \"MaxKeys\": 1000}\n        if token:\n            kwargs[\"ContinuationToken\"] = token\n        page = client.list_objects_v2(**kwargs)\n        for item in page.get(\"Contents\", []):\n            listed += 1\n            size = int(item[\"Size\"])\n            if size &amp;lt; min_size or size &amp;gt; max_size:\n                continue\n            kept += 1\n            yield item\n        if not page.get(\"IsTruncated\"):\n            break\n        token = page.get(\"NextContinuationToken\")\n        print(\n            f\"list progress listed={listed} candidates={kept}\",\n            file=sys.stderr,\n            flush=True,\n        )\n    print(\n        f\"list done listed={listed} candidates={kept}\",\n        file=sys.stderr,\n        flush=True,\n    )\n\n\ndef sample_one(\n    endpoint: str | None,\n    bucket: str,\n    item: dict,\n    head_bytes: int,\n    tail_bytes: int,\n) -&amp;gt; Hit | None:\n    client = client_for_thread(endpoint)\n    key = item[\"Key\"]\n    size = int(item[\"Size\"])\n    etag = str(item.get(\"ETag\", \"\"))\n    head_end = min(size, head_bytes) - 1\n    try:\n        head = get_range(client, bucket, key, 0, head_end)\n    except ClientError as error:\n        print(f\"warn: range get failed key={key}: {error}\", file=sys.stderr)\n        return None\n\n    hit = analyze_head(key, size, etag, head)\n    if hit is None:\n        return None\n    return enrich_tail(client, bucket, hit, size, head, tail_bytes)\n\n\ndef main() -&amp;gt; int:\n    args = parse_args()\n    if not args.bucket:\n        raise SystemExit(\"error: pass --bucket or set R2_BUCKET\")\n    if args.head_bytes &amp;lt; 520:\n        raise SystemExit(\"error: --head-bytes must be &amp;gt;= 520 to catch HDF5@512\")\n    if args.workers &amp;lt; 1:\n        raise SystemExit(\"error: --workers must be &amp;gt;= 1\")\n    if args.min_size &amp;lt; 0 or args.max_size &amp;lt; args.min_size:\n        raise SystemExit(\"error: invalid size bounds\")\n\n    list_client = make_client(args.endpoint)\n    candidates = []\n    for item in iter_candidates(\n        list_client,\n        args.bucket,\n        args.prefix,\n        args.min_size,\n        args.max_size,\n    ):\n        candidates.append(item)\n        if args.max_objects and len(candidates) &amp;gt;= args.max_objects:\n            break\n\n    hits: list[Hit] = []\n    sampled = 0\n    lock = threading.Lock()\n    jsonl = open(args.jsonl, \"w\", encoding=\"utf-8\") if args.jsonl else None\n\n    try:\n        with ThreadPoolExecutor(max_workers=args.workers) as pool:\n            futures = [\n                pool.submit(\n                    sample_one,\n                    args.endpoint,\n                    args.bucket,\n                    item,\n                    args.head_bytes,\n                    args.tail_bytes,\n                )\n                for item in candidates\n            ]\n            for future in as_completed(futures):\n                sampled += 1\n                if sampled % 200 == 0 or sampled == len(candidates):\n                    print(\n                        f\"sample progress sampled={sampled}/{len(candidates)} \"\n                        f\"hits={len(hits)}\",\n                        file=sys.stderr,\n                        flush=True,\n                    )\n                hit = future.result()\n                if hit is None:\n                    continue\n                with lock:\n                    hits.append(hit)\n                    line = json.dumps(\n                        asdict(hit), separators=(\",\", \":\"), sort_keys=True\n                    )\n                    print(line, flush=True)\n                    if jsonl is not None:\n                        jsonl.write(line + \"\\n\")\n                        jsonl.flush()\n    finally:\n        if jsonl is not None:\n            jsonl.close()\n\n    print(\n        f\"summary candidates={len(candidates)} sampled={sampled} hits={len(hits)} \"\n        f\"bucket={args.bucket} prefix={args.prefix!r} \"\n        f\"size_window={args.min_size}-{args.max_size}\",\n        file=sys.stderr,\n    )\n    if not hits:\n        print(\n            \"no MATLAB/HDF5 hybrid uploads found in size window. \"\n            \"If Active Storage keeps blob metadata in Postgres, also query \"\n            \"active_storage_blobs for content_type like image/bmp and \"\n            \"byte_size between ~1KiB and ~64KiB.\",\n            file=sys.stderr,\n        )\n    return 1 if hits else 0\n\n\nif __name__ == \"__main__\":\n    raise SystemExit(main())\n", "creation_timestamp": "2026-07-30T14:46:38.437223Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/c4e5e0c1-a918-43c6-ad6f-c5b4e2724842/export"/>
    <published>2026-07-30T14:46:38.437223+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/e0add544-36a2-4604-a9e6-4552fca00233/export</id>
    <title>e0add544-36a2-4604-a9e6-4552fca00233</title>
    <updated>2026-08-30T23:22:39.417559+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cve.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "e0add544-36a2-4604-a9e6-4552fca00233", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "GHSA-xr9x-r78c-5hrm", "type": "seen", "source": "https://bsky.app/profile/nixpkgssecuritychanges.gerbet.me/post/3mrukk3nzjy2o", "content": "[Backport release-26.05] mastodon: patch CVE-2026-66066/GHSA-xr9x-r78c-5hrm in activesupport gem\n\nhttps://github.com/NixOS/nixpkgs/pull/547370\n\n#security", "creation_timestamp": "2026-07-30T13:34:04.470219Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/e0add544-36a2-4604-a9e6-4552fca00233/export"/>
    <published>2026-07-30T13:34:04.470219+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/e3f0adf9-03ec-440c-965d-109e2f87f63c/export</id>
    <title>e3f0adf9-03ec-440c-965d-109e2f87f63c</title>
    <updated>2026-08-30T23:22:39.417669+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cve.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "e3f0adf9-03ec-440c-965d-109e2f87f63c", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "GHSA-xr9x-r78c-5hrm", "type": "seen", "source": "https://bsky.app/profile/nixpkgssecuritychanges.gerbet.me/post/3mrudy2kgin2f", "content": "mastodon: patch CVE-2026-66066/GHSA-xr9x-r78c-5hrm in activesupport gem\n\nhttps://github.com/NixOS/nixpkgs/pull/547199\n\n#security", "creation_timestamp": "2026-07-30T11:36:36.813453Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/e3f0adf9-03ec-440c-965d-109e2f87f63c/export"/>
    <published>2026-07-30T11:36:36.813453+00:00</published>
  </entry>
</feed>
