Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/security-middleware-capabilities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@croco/transports-http": patch
---

Validate HTTP security middleware through explicit capability metadata instead of source text.
38 changes: 34 additions & 4 deletions docs/problem-code-registry.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": "croco.problem-code-registry.v1",
"problemCount": 413,
"problemCount": 414,
"problems": [
{
"code": "ACCESS_DENIED",
Expand Down Expand Up @@ -2726,7 +2726,37 @@
"sources": [
{
"file": "packages/transports-http/src/libs/CrocoApp.ts",
"line": 195,
"line": 183,
"column": 11,
"kind": "problem-factory"
}
]
},
{
"code": "CROCO_HTTP_SECURITY_002",
"category": "BadRequest",
"status": 400,
"title": "Bad Request",
"cookbookPath": "/reference/problem-recovery-cookbook/#croco-http-security-002",
"recovery": {
"cause": "The caller sent malformed input or unsupported request options.",
"userAction": "Correct the request input and retry after validation passes.",
"operatorAction": "Inspect validation details and request logs; do not retry unchanged input.",
"retryability": "not-retryable",
"redactionPolicy": "public",
"telemetry": {
"eventName": "croco.problem.info",
"severity": "info",
"attributes": ["problem.code", "problem.category", "problem.status"]
}
},
"lifecycle": {
"status": "active"
},
"sources": [
{
"file": "packages/transports-http/src/libs/middleware/SecurityMiddlewareMarker.ts",
"line": 148,
"column": 11,
"kind": "problem-factory"
}
Expand Down Expand Up @@ -11066,7 +11096,7 @@
"sources": [
{
"file": "packages/transports-http/src/libs/CrocoApp.ts",
"line": 232,
"line": 220,
"column": 11,
"kind": "problem-factory"
}
Expand Down Expand Up @@ -11306,7 +11336,7 @@
"sources": [
{
"file": "packages/transports-http/src/libs/CrocoApp.ts",
"line": 81,
"line": 76,
"column": 55,
"kind": "problem-metadata"
}
Expand Down
17 changes: 15 additions & 2 deletions docs/troubleshooting/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ Search: CROCO_ROUTE_004, missing path param, @Param, route contract
| `CROCO_BUILD_002` | build-time | error | generated artifact가 source와 drift됨 | package-specific write command 실행 후 diff 검토 |
| `CROCO_BUILD_003` | build-time | error | controller source에 TypeScript 오류가 있음 | controller type error 수정 후 contract 재실행 |
| `CROCO_HTTP_SECURITY_001` | runtime | error | HTTP bootstrap에 필수 security middleware가 없음 | security headers, CORS, body limit, rate limit middleware 등록 |
| `CROCO_HTTP_SECURITY_002` | runtime | error | 지원하지 않는 security capability를 선언함 | supported capability literal로 수정 |

### CLI diagnostic code migration

Expand Down Expand Up @@ -273,14 +274,26 @@ Fix: 출력된 source file, line/column, `TS####` diagnostic을 기준으로 con
### `CROCO_HTTP_SECURITY_001`

Cause: HTTP app bootstrap이 `securityHeadersMiddleware`, `corsMiddleware`, `bodyLimitMiddleware`,
`rateLimitHttpMiddleware` 중 하나 이상이 빠진 상태를 발견했습니다.
Fix: 운영 및 generated app 기본 경로에서는 네 가지 middleware를 모두 등록합니다. 로컬 마이그레이션이나
`rateLimitHttpMiddleware` 중 하나 이상이 빠졌거나, custom/wrapper middleware가 명시적인 security
capability metadata를 선언하지 않은 상태를 발견했습니다. 검증은 middleware source text를 검사하지 않습니다.
Fix: 운영 및 generated app 기본 경로에서는 네 가지 built-in middleware를 모두 등록합니다. Custom 또는
wrapper middleware를 사용하는 경우 `declareSecurityMiddlewareCapabilities()`로 `security-headers`,
`cors`, `body-limit`, `rate-limit` 중 제공하는 capability를 선언하거나,
`getSecurityMiddlewareCapabilities()`로 감싼 middleware의 metadata를 복사합니다. 로컬 마이그레이션이나
테스트 fixture처럼 실패를 의도적으로 확인하는 경우에만 `securityValidation: "off"` 또는
`CROCO_HTTP_SECURITY_VALIDATION=off`를 사용하고, PR 설명이나 fixture 이름에 그 이유를 남깁니다.
이전 slash-form code인 `transports-http/security-middleware-validation`을 매칭하던 코드는
`CROCO_HTTP_SECURITY_001`로 옮기고, 전환 기간에는 Problem `extensions.legacyCode`에서 이전 값을
확인할 수 있습니다.

### `CROCO_HTTP_SECURITY_002`

Cause: `declareSecurityMiddlewareCapabilities()` 호출이 지원하지 않는 security capability literal을
받았습니다. 지원되는 값은 `security-headers`, `cors`, `body-limit`, `rate-limit`입니다.
Fix: Custom 또는 wrapper middleware 선언을 지원되는 literal 중 하나 이상으로 수정합니다. JavaScript
호출자는 오타가 runtime에서 `extensions.capability`와 함께 실패하므로, 해당 값을 기준으로 선언 코드를
고칩니다.

### 변경 정책

- 코드는 append-only입니다. 한번 공개된 `CROCO_*` 코드는 다른 의미로 재사용하거나 이름을 바꾸지 않습니다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@ pnpm build:api && pnpm build:web

`apps/api-server/src/app.ts`는 첫 실행부터 `@croco/transports-http`의 보안 헤더, CORS,
본문 크기 제한, rate limit middleware를 등록합니다. 이 경로는 기본 `securityValidation:
"enforce"` 계약을 통과해야 하며, 누락되면 `CROCO_HTTP_SECURITY_001` 진단으로 실패합니다.
"enforce"` 계약을 통과해야 하며, 누락되거나 명시적인 middleware capability metadata가 없으면
`CROCO_HTTP_SECURITY_001` 진단으로 실패합니다. Generated app의 built-in middleware는 이 metadata를
자동으로 선언하므로 marker-only 검증을 통과합니다.

로컬 마이그레이션이나 테스트 fixture에서 누락된 middleware 실패를 의도적으로 확인할 때만
`securityValidation: "off"` 또는 `CROCO_HTTP_SECURITY_VALIDATION=off`를 임시로 사용하세요.
운영 경로에서는 CORS origin, body limit, rate limit 정책을 서비스 요구사항에 맞게 조정하고
Custom 또는 wrapper middleware를 사용할 때는 `declareSecurityMiddlewareCapabilities()`로 capability를
선언하고, 운영 경로에서는 CORS origin, body limit, rate limit 정책을 서비스 요구사항에 맞게 조정하며
검증은 켜 둡니다.

## 배포
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
editUrl: false
next: false
prev: false
title: "declareSecurityMiddlewareCapabilities"
---

> **declareSecurityMiddlewareCapabilities**(`middleware`, `capabilities`): [`MiddlewareFunction`](/api/transports-http/src/type-aliases/middlewarefunction/)

Declares the security capability provided by a custom or wrapped HTTP middleware.

## Parameters

### middleware

[`MiddlewareFunction`](/api/transports-http/src/type-aliases/middlewarefunction/)

### capabilities

readonly [`SecurityMiddlewareCapability`](/api/transports-http/src/type-aliases/securitymiddlewarecapability/)[]

## Returns

[`MiddlewareFunction`](/api/transports-http/src/type-aliases/middlewarefunction/)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
editUrl: false
next: false
prev: false
title: "getSecurityMiddlewareCapabilities"
---

> **getSecurityMiddlewareCapabilities**(`middleware`): readonly [`SecurityMiddlewareCapability`](/api/transports-http/src/type-aliases/securitymiddlewarecapability/)[]

Returns a deterministic immutable copy of the declared security capabilities.

## Parameters

### middleware

[`MiddlewareFunction`](/api/transports-http/src/type-aliases/middlewarefunction/)

## Returns

readonly [`SecurityMiddlewareCapability`](/api/transports-http/src/type-aliases/securitymiddlewarecapability/)[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
editUrl: false
next: false
prev: false
title: "hasSecurityMiddlewareCapability"
---

> **hasSecurityMiddlewareCapability**(`middleware`, `capability`): `boolean`

Checks whether a middleware declares a security capability.

## Parameters

### middleware

[`MiddlewareFunction`](/api/transports-http/src/type-aliases/middlewarefunction/)

### capability

[`SecurityMiddlewareCapability`](/api/transports-http/src/type-aliases/securitymiddlewarecapability/)

## Returns

`boolean`
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
editUrl: false
next: false
prev: false
title: "SecurityMiddlewareCapability"
---

> **SecurityMiddlewareCapability** = `"security-headers"` \| `"cors"` \| `"body-limit"` \| `"rate-limit"`

Security capability literals recognized by HTTP bootstrap validation.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: Generated Croco Problem code registry with recovery and telemetry m

> Generated by `pnpm problem-registry:write`. Do not edit this file by hand.

This cookbook documents 413 public Croco Problem codes. The deterministic JSON registry is generated at `docs/problem-code-registry.json`, and generated client union types are emitted at `packages/problems-core/src/generated/problem-code-registry.ts`.
This cookbook documents 414 public Croco Problem codes. The deterministic JSON registry is generated at `docs/problem-code-registry.json`, and generated client union types are emitted at `packages/problems-core/src/generated/problem-code-registry.ts`.

## Index

Expand Down Expand Up @@ -104,6 +104,7 @@ This cookbook documents 413 public Croco Problem codes. The deterministic JSON r
| [`CROCO_CLI_OPS_002`](#croco-cli-ops-002) | BadRequest | 400 | not-retryable | public | active | 1 |
| [`CROCO_CLI_USAGE_DASHBOARD_005`](#croco-cli-usage-dashboard-005) | BadRequest | 400 | not-retryable | public | active | 1 |
| [`CROCO_HTTP_SECURITY_001`](#croco-http-security-001) | InternalServerError | 500 | not-retryable | public | active | 1 |
| [`CROCO_HTTP_SECURITY_002`](#croco-http-security-002) | BadRequest | 400 | not-retryable | public | active | 1 |
| [`dataloader-core/batch-result-length-mismatch`](#dataloader-core-batch-result-length-mismatch) | InternalServerError | 500 | conditional | operator-only | active | 1 |
| [`diagnostics-core/duplicate-provider`](#diagnostics-core-duplicate-provider) | InternalServerError | 500 | conditional | operator-only | active | 1 |
| [`DUPLICATE_INVITATION`](#duplicate-invitation) | Conflict | 409 | conditional | safe-message | active | 1 |
Expand Down Expand Up @@ -2063,7 +2064,25 @@ Sources:

Sources:

- `packages/transports-http/src/libs/CrocoApp.ts:195:11` (problem-factory)
- `packages/transports-http/src/libs/CrocoApp.ts:183:11` (problem-factory)

<a id="croco-http-security-002"></a>

## `CROCO_HTTP_SECURITY_002`

- Category: `BadRequest`
- HTTP status: `400` Bad Request
- Retryability: `not-retryable`
- Redaction policy: `public`
- Lifecycle: `active`
- Cause: The caller sent malformed input or unsupported request options.
- User action: Correct the request input and retry after validation passes.
- Operator action: Inspect validation details and request logs; do not retry unchanged input.
- Telemetry: `croco.problem.info` (info) with `problem.code`, `problem.category`, `problem.status`

Sources:

- `packages/transports-http/src/libs/middleware/SecurityMiddlewareMarker.ts:148:11` (problem-factory)

<a id="dataloader-core-batch-result-length-mismatch"></a>

Expand Down Expand Up @@ -7067,7 +7086,7 @@ Sources:

Sources:

- `packages/transports-http/src/libs/CrocoApp.ts:232:11` (problem-factory)
- `packages/transports-http/src/libs/CrocoApp.ts:220:11` (problem-factory)

<a id="transports-http-duplicate-health-check"></a>

Expand Down Expand Up @@ -7211,7 +7230,7 @@ Sources:

Sources:

- `packages/transports-http/src/libs/CrocoApp.ts:81:55` (problem-metadata)
- `packages/transports-http/src/libs/CrocoApp.ts:76:55` (problem-metadata)

<a id="transports-http-unsupported-route-method"></a>

Expand Down
39 changes: 35 additions & 4 deletions packages/problems-core/src/generated/problem-code-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { ProblemCodeRegistry } from "../libs/ProblemRegistry";

export const CROCO_PROBLEM_CODE_REGISTRY = {
version: "croco.problem-code-registry.v1",
problemCount: 413,
problemCount: 414,
problems: [
{
code: "ACCESS_DENIED",
Expand Down Expand Up @@ -2826,7 +2826,38 @@ export const CROCO_PROBLEM_CODE_REGISTRY = {
sources: [
{
file: "packages/transports-http/src/libs/CrocoApp.ts",
line: 195,
line: 183,
column: 11,
kind: "problem-factory",
},
],
},
{
code: "CROCO_HTTP_SECURITY_002",
category: "BadRequest",
status: 400,
title: "Bad Request",
cookbookPath: "/reference/problem-recovery-cookbook/#croco-http-security-002",
recovery: {
cause: "The caller sent malformed input or unsupported request options.",
userAction: "Correct the request input and retry after validation passes.",
operatorAction:
"Inspect validation details and request logs; do not retry unchanged input.",
retryability: "not-retryable",
redactionPolicy: "public",
telemetry: {
eventName: "croco.problem.info",
severity: "info",
attributes: ["problem.code", "problem.category", "problem.status"],
},
},
lifecycle: {
status: "active",
},
sources: [
{
file: "packages/transports-http/src/libs/middleware/SecurityMiddlewareMarker.ts",
line: 148,
column: 11,
kind: "problem-factory",
},
Expand Down Expand Up @@ -11542,7 +11573,7 @@ export const CROCO_PROBLEM_CODE_REGISTRY = {
sources: [
{
file: "packages/transports-http/src/libs/CrocoApp.ts",
line: 232,
line: 220,
column: 11,
kind: "problem-factory",
},
Expand Down Expand Up @@ -11803,7 +11834,7 @@ export const CROCO_PROBLEM_CODE_REGISTRY = {
sources: [
{
file: "packages/transports-http/src/libs/CrocoApp.ts",
line: 81,
line: 76,
column: 55,
kind: "problem-metadata",
},
Expand Down
Loading
Loading