openapi: 3.2.0 info: title: Ironfang Security Scans API version: 0.1.0 summary: External Security Check - verify a domain, run a low-impact external assessment, fix and recheck findings. description: 'Ironfang Security helps an organisation understand what it exposes to the internet, fix what matters and keep evidence that it did.' contact: name: Ironfang email: hello@ironfang.uk url: https://ironfang.com/contact termsOfService: https://ironfang.com/legal/terms license: name: Proprietary - use governed by the Ironfang terms of service url: https://ironfang.com/legal/terms servers: - url: https://api.ironfang.com/security security: - bearerAuth: [] tags: - name: Scans description: External Security Check runs and their results. paths: /v1/domains/{domainId}/scans: parameters: - $ref: '#/components/parameters/DomainId' post: tags: - Scans operationId: startScan summary: Run the External Security Check description: Queues a full scan of a verified, authorised domain. The verification record is looked up again first; if it has been removed the domain returns to unverified and the scan is refused. Subject to a cooldown per domain, a daily limit and one running scan per organisation. responses: '202': description: The queued scan. content: application/json: schema: $ref: '#/components/schemas/Scan' '404': $ref: '#/components/responses/Problem' '409': $ref: '#/components/responses/Problem' '429': $ref: '#/components/responses/Problem' default: $ref: '#/components/responses/Problem' /v1/scans: get: tags: - Scans operationId: listScans summary: Scan history parameters: - name: domain_id in: query required: false schema: type: string format: uuid - $ref: '#/components/parameters/Cursor' - $ref: '#/components/parameters/Limit' responses: '200': description: Scans, newest first. content: application/json: schema: type: object required: - items - next_cursor properties: items: type: array items: $ref: '#/components/schemas/ScanSummary' next_cursor: type: string description: Opaque cursor for the next page; empty on the last page. default: $ref: '#/components/responses/Problem' /v1/scans/{scanId}: parameters: - $ref: '#/components/parameters/ScanId' get: tags: - Scans operationId: getScan summary: Get a scan and its results responses: '200': description: The scan. content: application/json: schema: $ref: '#/components/schemas/Scan' '404': $ref: '#/components/responses/Problem' default: $ref: '#/components/responses/Problem' /v1/scans/{scanId}/cancel: parameters: - $ref: '#/components/parameters/ScanId' post: tags: - Scans operationId: cancelScan summary: Cancel a queued or running scan responses: '200': description: The scan after cancellation. content: application/json: schema: $ref: '#/components/schemas/Scan' '404': $ref: '#/components/responses/Problem' '409': $ref: '#/components/responses/Problem' default: $ref: '#/components/responses/Problem' components: schemas: PassedCheck: type: object required: - check_id - title - category - host - summary - docs_url properties: check_id: type: string title: type: string category: $ref: '#/components/schemas/CheckCategory' host: type: string summary: type: string docs_url: type: string format: uri ScanSummary: type: object required: - id - domain_id - domain - kind - status - requested_at - progress - counts properties: id: type: string format: uuid domain_id: type: string format: uuid domain: type: string kind: $ref: '#/components/schemas/ScanKind' status: $ref: '#/components/schemas/ScanStatus' requested_at: type: string format: date-time started_at: type: string format: date-time finished_at: type: string format: date-time duration_ms: type: integer progress: type: object required: - total - completed properties: total: type: integer completed: type: integer counts: $ref: '#/components/schemas/ScanCounts' changes: $ref: '#/components/schemas/ScanChanges' recheck_finding_id: type: string format: uuid status_reason: type: string description: Why a scan was cancelled, stopped or failed. Scan: allOf: - $ref: '#/components/schemas/ScanSummary' - type: object required: - policy_version - categories - findings - passed_checks - observations - errors - limitations - recommended_actions - scanner - disclaimer properties: policy_version: type: string requested_by: $ref: '#/components/schemas/Person' categories: type: array items: $ref: '#/components/schemas/CategoryResult' findings: type: array description: Findings observed in this scan. items: $ref: '#/components/schemas/FindingSummary' passed_checks: type: array items: $ref: '#/components/schemas/PassedCheck' observations: type: array description: Informational results that are neither a pass nor a finding (e.g. detected technologies). items: $ref: '#/components/schemas/CheckNote' errors: type: array description: Checks that could not complete. items: $ref: '#/components/schemas/CheckNote' limitations: type: array items: type: string recommended_actions: type: array items: $ref: '#/components/schemas/RecommendedAction' scanner: $ref: '#/components/schemas/ScannerIdentity' disclaimer: type: string FindingStatus: type: string enum: - open - acknowledged - fixed - accepted_risk - false_positive - needs_review ScanChanges: type: object required: - new - fixed - reopened properties: new: type: integer fixed: type: integer reopened: type: integer FindingKind: type: string enum: - issue - hardening - informational - potential_vulnerability CategoryResult: type: object required: - category - title - status - passed - findings - errors properties: category: $ref: '#/components/schemas/CheckCategory' title: type: string status: type: string enum: - pending - running - completed - failed - cancelled passed: type: integer findings: type: integer errors: type: integer CheckNote: type: object required: - check_id - title - host - message properties: check_id: type: string title: type: string host: type: string message: type: string Severity: type: string enum: - critical - high - medium - low - info CheckCategory: type: string enum: - dns - tls - http - email - exposure - technology - discovery ScanStatus: type: string enum: - queued - running - completed - partial - failed - cancelled - killed Confidence: type: string description: How sure Ironfang is. `low` is presented as "potential". enum: - confirmed - high - medium - low FindingSummary: type: object required: - id - key - title - category - kind - severity - confidence - status - host - domain_id - first_seen_at - last_seen_at - verification properties: id: type: string format: uuid key: type: string description: Catalogue key, e.g. http.hsts_missing. title: type: string category: $ref: '#/components/schemas/CheckCategory' kind: $ref: '#/components/schemas/FindingKind' severity: $ref: '#/components/schemas/Severity' confidence: $ref: '#/components/schemas/Confidence' status: $ref: '#/components/schemas/FindingStatus' host: type: string domain_id: type: string format: uuid detail: type: string description: What was observed on this host. first_seen_at: type: string format: date-time last_seen_at: type: string format: date-time resolved_at: type: string format: date-time verification: type: string description: Whether a scan or recheck has confirmed the current state. enum: - unverified - fixed_verified - still_present Problem: type: object required: - error - request_id properties: error: type: object required: - code - message properties: code: type: string description: 'Stable error code. Among them: `unauthorized`, `forbidden`, `not_found`, `validation_failed`, `domain_invalid`, `domain_exists`, `domain_limit_reached`, `verification_pending_limit`, `verification_required`, `already_verified`, `authorisation_required`, `policy_outdated`, `scan_in_progress`, `scan_cooldown`, `daily_limit_reached`, `domain_rate_limited`, `scan_not_active`, `recheck_unavailable`, `recheck_cooldown`, `check_rate_limited`, `organisation_suspended`, `scans_paused`, `security_disabled`, `verification_expired`, `verification_unavailable`, `verification_pending_limit`, `finding_fixed`, `internal`. From the Ironfang API in front of the service: `api_keys_not_supported`, `permissions_unavailable`, `body_too_large`, `invalid_body`, `invalid_tenant`, `security_unavailable`, `security_timeout`. ' message: type: string docs: type: string format: uri retry_after_seconds: type: integer description: Set on cooldowns and rate limits. request_id: type: string ScanKind: type: string enum: - full - recheck ScanCounts: type: object required: - critical - high - medium - low - info - passed - confirmed - unverified - errors properties: critical: type: integer high: type: integer medium: type: integer low: type: integer info: type: integer passed: type: integer description: Checks that observed a control working. confirmed: type: integer description: Findings with confirmed or high confidence. unverified: type: integer description: Findings with medium or low confidence. errors: type: integer description: Checks that could not complete. Person: type: object required: - label properties: label: type: string RecommendedAction: type: object required: - finding_id - title - severity - confidence - host - domain_id - reason properties: finding_id: type: string format: uuid title: type: string severity: $ref: '#/components/schemas/Severity' confidence: $ref: '#/components/schemas/Confidence' host: type: string domain_id: type: string format: uuid reason: type: string ScannerIdentity: type: object required: - user_agent - info_url - source_addresses properties: user_agent: type: string info_url: type: string format: uri source_addresses: type: array description: Public addresses scanner traffic comes from, for allowlisting. items: type: string parameters: Cursor: name: cursor in: query required: false description: The next_cursor of the previous page. schema: type: string Limit: name: limit in: query required: false schema: type: integer minimum: 1 maximum: 100 default: 25 ScanId: name: scanId in: path required: true schema: type: string format: uuid DomainId: name: domainId in: path required: true schema: type: string format: uuid responses: Problem: description: An error. content: application/json: schema: $ref: '#/components/schemas/Problem' securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: Warden JWT description: 'A Warden user token. Each route also requires a tenant permission in the acting tenant (`X-Ironfang-Tenant`), resolved live from Warden: reads need `security.read`; everything that changes state needs `security.write`. A missing permission is 403; a permission lookup failure is 503. Platform API keys are not accepted yet. ' externalDocs: description: Ironfang Security documentation url: https://ironfang.com/security/docs