fix: validate HTTP dates IPv6 URLs and parser errors
CSS quality / css-quality (push) Has been cancelled
CSS quality / css-quality (push) Has been cancelled
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { createHash, createHmac } from 'node:crypto';
|
||||
import { HttpException } from '@nestjs/common';
|
||||
import { BadRequestException, HttpException } from '@nestjs/common';
|
||||
import type { LookupFunction } from 'node:net';
|
||||
|
||||
/** Keep the validated address pinned while honoring Node's all-address lookup contract. */
|
||||
@@ -10,6 +10,60 @@ export function pinnedWebhookLookup(address: string, family: number): LookupFunc
|
||||
};
|
||||
}
|
||||
|
||||
/** Validate calendar components before Date can normalize an impossible day. */
|
||||
export function parseOpenApiDate(value: string): Date {
|
||||
const parts = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?(Z|[+-]\d{2}:\d{2}))?$/.exec(
|
||||
value,
|
||||
);
|
||||
if (parts) {
|
||||
const year = Number(parts[1]);
|
||||
const month = Number(parts[2]);
|
||||
const day = Number(parts[3]);
|
||||
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
||||
const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||
const zone = parts[8];
|
||||
const validZone = !zone || zone === 'Z' || (Number(zone.slice(1, 3)) < 24 && Number(zone.slice(4)) < 60);
|
||||
const date = new Date(value);
|
||||
if (
|
||||
month >= 1 &&
|
||||
month <= 12 &&
|
||||
day >= 1 &&
|
||||
day <= days[month - 1] &&
|
||||
Number(parts[4] ?? 0) < 24 &&
|
||||
Number(parts[5] ?? 0) < 60 &&
|
||||
Number(parts[6] ?? 0) < 60 &&
|
||||
validZone &&
|
||||
Number.isFinite(date.getTime())
|
||||
)
|
||||
return date;
|
||||
}
|
||||
throw new BadRequestException({ code: 'TIME_RANGE_INVALID', message: '时间必须为有效的ISO8601日期或带时区时间' });
|
||||
}
|
||||
|
||||
export type OpenApiProblemResponse = {
|
||||
setHeader(name: string, value: string): void;
|
||||
status(code: number): { type(value: string): { send(body: unknown): void } };
|
||||
};
|
||||
|
||||
export function sendOpenApiProblem(
|
||||
response: OpenApiProblemResponse,
|
||||
requestId: string,
|
||||
failure: { status: number; code: string; message: string },
|
||||
) {
|
||||
response.setHeader('X-Request-Id', requestId);
|
||||
response
|
||||
.status(failure.status)
|
||||
.type('application/problem+json')
|
||||
.send({
|
||||
type: `https://cmpp-platform.local/problems/${failure.code.toLowerCase()}`,
|
||||
title: failure.status >= 500 ? 'Internal Server Error' : 'Request failed',
|
||||
status: failure.status,
|
||||
code: failure.code,
|
||||
detail: failure.message,
|
||||
requestId,
|
||||
});
|
||||
}
|
||||
|
||||
/** v1 compatibility: an absent parsed body hashes as {}, never try alternate hashes. */
|
||||
export function openApiBodyHash(rawBody: Buffer | undefined, body: unknown) {
|
||||
return createHash('sha256')
|
||||
|
||||
Reference in New Issue
Block a user