openapi: 3.0.3
info:
  title: md-safe
  version: 1.0.0
  description: >-
    Безопасный рендер Markdown в HTML. Собственный парсер подмножества CommonMark и санитайзер по allowlist:
    разрешённые теги, атрибуты и схемы URL, rel="noopener noreferrer nofollow ugc" на всех ссылках,
    javascript: и data: блокируются (data: только для картинок по настройке). Демо-проект.
  contact:
    name: Ewsir
    url: https://github.com/ewsir247
servers:
  - url: http://localhost:8120
paths:
  /healthz:
    get:
      summary: Проверка живости
      responses:
        '200':
          description: Сервис работает
          content:
            application/json:
              schema: { type: object, properties: { status: { type: string, example: ok } } }
  /v1/render:
    post:
      summary: Markdown в безопасный HTML
      description: >-
        Тело application/json с полем markdown даёт JSON-ответ. Тело text/markdown (или text/plain) даёт text/html;
        параметры задаются query-строкой raw_html, allow_images, allow_data_images. Ответ text/html отдаётся с
        CSP default-src 'none'; sandbox.
      parameters:
        - in: query
          name: raw_html
          schema: { type: string, enum: [sanitize, escape], default: sanitize }
        - in: query
          name: allow_images
          schema: { type: boolean, default: true }
        - in: query
          name: allow_data_images
          schema: { type: boolean, default: false }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [markdown]
              properties:
                markdown: { type: string }
                options: { $ref: '#/components/schemas/Options' }
          text/markdown:
            schema: { type: string }
      responses:
        '200':
          description: Очищенный HTML
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Result' }
            text/html:
              schema: { type: string }
        '413': { $ref: '#/components/responses/Error' }
        '415': { $ref: '#/components/responses/Error' }
        '422': { $ref: '#/components/responses/Error' }
        '429': { $ref: '#/components/responses/Error' }
  /v1/sanitize:
    post:
      summary: Очистить готовый HTML
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [html]
              properties:
                html: { type: string }
                options: { $ref: '#/components/schemas/Options' }
      responses:
        '200':
          description: Очищенный HTML. Повторная очистка результата ничего не меняет.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Result' }
        '413': { $ref: '#/components/responses/Error' }
        '415': { $ref: '#/components/responses/Error' }
        '422': { $ref: '#/components/responses/Error' }
        '429': { $ref: '#/components/responses/Error' }
  /v1/policy:
    get:
      summary: Действующий allowlist
      responses:
        '200':
          description: Теги, атрибуты, схемы URL
          content:
            application/json:
              schema: { type: object }
components:
  responses:
    Error:
      description: Ошибка
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: object
                properties:
                  code: { type: string }
                  message: { type: string }
  schemas:
    Options:
      type: object
      properties:
        raw_html:
          type: string
          enum: [sanitize, escape]
          default: sanitize
          description: sanitize пропускает сырой HTML через санитайзер, escape показывает его как текст (только для /v1/render)
        allow_images: { type: boolean, default: true }
        allow_data_images:
          type: boolean
          default: false
          description: Разрешить data:image/(png|gif|jpeg|webp|avif);base64 в src картинок
    Result:
      type: object
      properties:
        html: { type: string }
        report:
          type: object
          properties:
            removed_tags: { type: array, items: { type: string } }
            removed_attrs: { type: array, items: { type: string } }
            blocked_urls: { type: integer }
        stats:
          type: object
          properties:
            input_chars: { type: integer }
            output_chars: { type: integer }
            ms: { type: number }
