Files
crumbs-original/static/api/openapi.json
T
Frederic CabassutandClaude Sonnet 5 d4ed358ba3 fix(security): patch dependency CVEs and close SSRF/XSS gaps in MCP image upload
Bumps deps to clear all 69 pnpm audit findings (nodemailer 8->9 for the
raw-option SSRF/arbitrary-file-read CVE; svelte, hono/express/qs chain,
linkify-it, and the rest resolved within their existing semver ranges
via a lockfile refresh). Caps the previously-unbounded cookie override
at <1 so it can't silently jump to the API-incompatible 2.x line again.

Hardens the MCP upload_image tool, which fetched an arbitrary caller-
supplied URL and stored whatever content-type came back with no checks:
- validates every resolved DNS record (not just the first) against
  private/loopback/link-local/multicast ranges, then pins the actual
  connection to the validated address so a second, unvalidated lookup
  at connect time can't be used for DNS-rebinding SSRF
- rejects non-image content-types and enforces the same 10MB cap as
  the manual upload endpoint
- adds X-Content-Type-Options: nosniff to both attachment-serving
  routes as defense in depth

Also validates the admin role field server-side (previously trusted
the client-supplied string), and fixes two incidental regressions
surfaced by the dependency bump: StarterKit now bundles its own
Link/Underline extensions (disabled to avoid duplicate registration),
and FormattingToolbar could call methods on an already-destroyed
editor instance during the markdown-mode toggle race.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 13:43:25 +02:00

890 lines
16 KiB
JSON

{
"openapi": "3.1.0",
"info": {
"title": "Crumbs API",
"description": "Auto-generated API spec for crumbs",
"version": "0.31.0",
"contact": {
"name": "Bretzel",
"url": "https://bretzel.app"
}
},
"servers": [
{
"url": "/",
"description": "Current instance"
}
],
"security": [
{
"sessionCookie": []
}
],
"paths": {
"/api/admin/users": {
"get": {
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Success"
}
}
},
"post": {
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/admin/users/{id}": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"patch": {
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"delete": {
"tags": [
"Admin"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/auth/account": {
"delete": {
"tags": [
"Auth"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/auth/login": {
"post": {
"tags": [
"Auth"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/auth/logout": {
"post": {
"tags": [
"Auth"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/auth/oauth/{provider}": {
"parameters": [
{
"name": "provider",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"get": {
"tags": [
"Auth"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/auth/oauth/{provider}/callback": {
"parameters": [
{
"name": "provider",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"get": {
"tags": [
"Auth"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/auth/password": {
"put": {
"tags": [
"Auth"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/auth/profile": {
"put": {
"tags": [
"Auth"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/auth/sessions": {
"get": {
"tags": [
"Auth"
],
"responses": {
"200": {
"description": "Success"
}
}
},
"delete": {
"tags": [
"Auth"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/auth/setup": {
"post": {
"tags": [
"Auth"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/mcp": {
"post": {
"tags": [
"Mcp"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"get": {
"tags": [
"Mcp"
],
"responses": {
"200": {
"description": "Success"
}
}
},
"delete": {
"tags": [
"Mcp"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/notes": {
"get": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
}
},
"post": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/notes/reorder": {
"post": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/notes/{id}": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"get": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
}
},
"patch": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"delete": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/notes/{id}/attachments": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"get": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
}
},
"post": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"patch": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"delete": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/notes/{id}/collaborators": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"get": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
}
},
"post": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"delete": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/notes/{id}/share": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"post": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"delete": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/notes/{id}/versions": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"get": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/notes/{id}/versions/{versionId}": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "versionId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"get": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/notes/{id}/versions/{versionId}/restore": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "versionId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"post": {
"tags": [
"Notes"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/preferences": {
"get": {
"tags": [
"Preferences"
],
"responses": {
"200": {
"description": "Success"
}
}
},
"put": {
"tags": [
"Preferences"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/search": {
"get": {
"tags": [
"Search"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/settings/api-keys": {
"get": {
"tags": [
"Settings"
],
"responses": {
"200": {
"description": "Success"
}
}
},
"post": {
"tags": [
"Settings"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
},
"/api/settings/api-keys/{id}": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"delete": {
"tags": [
"Settings"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/shared/{token}": {
"parameters": [
{
"name": "token",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"get": {
"tags": [
"Shared"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/shared/{token}/attachment/{attachmentId}": {
"parameters": [
{
"name": "token",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "attachmentId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"get": {
"tags": [
"Shared"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/sync": {
"post": {
"tags": [
"Sync"
],
"responses": {
"200": {
"description": "Success"
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"get": {
"tags": [
"Sync"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/tags": {
"get": {
"tags": [
"Tags"
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/users/search": {
"get": {
"tags": [
"Users"
],
"responses": {
"200": {
"description": "Success"
}
}
}
}
},
"components": {
"securitySchemes": {
"sessionCookie": {
"type": "apiKey",
"in": "cookie",
"name": "session"
}
}
}
}