{
  "openapi": "3.1.0",
  "info": {
    "title": "uc.surf data",
    "version": "1.0.0",
    "summary": "Read-only JSON files with the public findings and blog posts published on uc.surf.",
    "description": "Every public finding is also published as JSON. The files are static and read-only, with no key and no sign-up. Pages, JSON and feed are built from the same data, so they always match. Only publicly disclosed findings are included. A finding's `id` is also its path: `<category>/<ecosystem>/<owner>__<repo>/<YYYY-MM-DD>-<slug>`.",
    "contact": {
      "name": "uc.surf",
      "url": "https://uc.surf/data/",
      "email": "info@uc.surf"
    }
  },
  "externalDocs": {
    "description": "Data and API",
    "url": "https://uc.surf/data/"
  },
  "servers": [
    {
      "url": "https://uc.surf"
    }
  ],
  "security": [],
  "paths": {
    "/data/findings/index.json": {
      "get": {
        "operationId": "listFindings",
        "summary": "Every public finding, newest first",
        "responses": {
          "200": {
            "description": "The findings index.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FindingsIndex"
                }
              }
            }
          }
        }
      }
    },
    "/data/findings/stats.json": {
      "get": {
        "operationId": "getFindingStats",
        "summary": "Counts by category, severity, status and ecosystem",
        "responses": {
          "200": {
            "description": "Finding counts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FindingStats"
                }
              }
            }
          }
        }
      }
    },
    "/data/findings/{category}/{ecosystem}/{project}/{finding}.json": {
      "get": {
        "operationId": "getFinding",
        "summary": "One finding, the full record",
        "description": "The path is the finding's `id` plus `.json`. The index lists it as `json_url`.",
        "parameters": [
          {
            "name": "category",
            "in": "path",
            "required": true,
            "description": "Finding category.",
            "schema": {
              "type": "string",
              "enum": [
                "security",
                "bugs",
                "docs",
                "performance"
              ]
            },
            "example": "bugs"
          },
          {
            "name": "ecosystem",
            "in": "path",
            "required": true,
            "description": "Package ecosystem, for example `go` or `rust`.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9+#.-]+$"
            },
            "example": "rust"
          },
          {
            "name": "project",
            "in": "path",
            "required": true,
            "description": "`<owner>__<repo>` of the project.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9-]+__[A-Za-z0-9._-]+$"
            },
            "example": "RustCrypto__signatures"
          },
          {
            "name": "finding",
            "in": "path",
            "required": true,
            "description": "`<YYYY-MM-DD>-<slug>`: the date it was found and a short name.",
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"
            },
            "example": "2026-10-04-xmssmt-h40-keyexhausted-not-detected"
          }
        ],
        "responses": {
          "200": {
            "description": "The finding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "https://uc.surf/data/findings/schema.json"
                }
              }
            }
          },
          "404": {
            "description": "There is no public finding with this id."
          }
        }
      }
    },
    "/data/posts/index.json": {
      "get": {
        "operationId": "listPosts",
        "summary": "Every blog post, newest first",
        "responses": {
          "200": {
            "description": "The posts index.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostsIndex"
                }
              }
            }
          }
        }
      }
    },
    "/data/findings/schema.json": {
      "get": {
        "operationId": "getFindingSchema",
        "summary": "The JSON Schema for a finding",
        "responses": {
          "200": {
            "description": "JSON Schema (draft 2020-12).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "FindingsIndex": {
        "type": "object",
        "required": [
          "generated_at",
          "count",
          "public_count",
          "schema",
          "findings"
        ],
        "properties": {
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "count": {
            "type": "integer",
            "minimum": 0
          },
          "public_count": {
            "type": "integer",
            "minimum": 0
          },
          "schema": {
            "type": "string",
            "format": "uri",
            "description": "The finding JSON Schema."
          },
          "findings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FindingSummary"
            }
          }
        }
      },
      "FindingSummary": {
        "type": "object",
        "required": [
          "id",
          "path",
          "url",
          "json_url",
          "title",
          "category",
          "severity",
          "status",
          "public",
          "ecosystem",
          "project",
          "summary",
          "disclosed_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "$ref": "https://uc.surf/data/findings/schema.json#/properties/id"
          },
          "path": {
            "type": "string",
            "description": "The JSON file, relative to /data/findings/."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The finding's page."
          },
          "json_url": {
            "type": "string",
            "format": "uri",
            "description": "The full JSON record."
          },
          "title": {
            "$ref": "https://uc.surf/data/findings/schema.json#/properties/title"
          },
          "category": {
            "$ref": "https://uc.surf/data/findings/schema.json#/properties/category"
          },
          "severity": {
            "$ref": "https://uc.surf/data/findings/schema.json#/properties/severity"
          },
          "status": {
            "$ref": "https://uc.surf/data/findings/schema.json#/properties/status"
          },
          "public": {
            "const": true
          },
          "ecosystem": {
            "$ref": "https://uc.surf/data/findings/schema.json#/properties/ecosystem"
          },
          "project": {
            "$ref": "https://uc.surf/data/findings/schema.json#/properties/project"
          },
          "summary": {
            "$ref": "https://uc.surf/data/findings/schema.json#/properties/summary"
          },
          "disclosed_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PostsIndex": {
        "type": "object",
        "required": [
          "generated_at",
          "count",
          "posts"
        ],
        "properties": {
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "count": {
            "type": "integer",
            "minimum": 0
          },
          "posts": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "slug",
                "url",
                "markdown_url",
                "title",
                "description",
                "tags",
                "published_at",
                "updated_at",
                "reading_minutes"
              ],
              "properties": {
                "slug": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri",
                  "description": "The post's page."
                },
                "markdown_url": {
                  "type": "string",
                  "format": "uri",
                  "description": "The post as Markdown."
                },
                "title": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "tags": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "published_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "updated_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "reading_minutes": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      },
      "FindingStats": {
        "type": "object",
        "required": [
          "generated_at",
          "total",
          "public",
          "private",
          "projects",
          "by_category",
          "by_severity",
          "by_status",
          "by_ecosystem"
        ],
        "properties": {
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "total": {
            "type": "integer",
            "minimum": 0
          },
          "public": {
            "type": "integer",
            "minimum": 0
          },
          "private": {
            "const": 0,
            "description": "Always 0: only public findings are published."
          },
          "projects": {
            "type": "integer",
            "minimum": 0
          },
          "by_category": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            }
          },
          "by_severity": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            }
          },
          "by_status": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            }
          },
          "by_ecosystem": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            }
          }
        }
      }
    }
  }
}
