Use re2 for identifier validation

This commit is contained in:
Fang-Pen Lin
2025-11-07 11:01:20 -08:00
parent 97b7e6c6bf
commit e5168e88e4
3 changed files with 14 additions and 14 deletions

View File

@@ -1,3 +1,5 @@
import { getConfig } from "@app/lib/config/env";
import RE2 from "re2";
import { z } from "zod";
import { getConfig } from "@app/lib/config/env";
@@ -17,3 +19,10 @@ export const extractAccountIdFromKid = (kid: string, profileId: string): string
}
return z.string().uuid().parse(kid.slice(kidPrefix.length));
};
export const validateDnsIdentifier = (identifier: string): boolean => {
// DNS label pattern: 1-63 chars, alphanumeric or hyphen, but not starting or ending with hyphen
const labelPattern = new RE2(/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/);
const labels = identifier.split(".");
return labels.every((label) => label.length >= 1 && label.length <= 63 && labelPattern.test(label));
};

View File

@@ -1,4 +1,3 @@
import RE2 from "re2";
import { z } from "zod";
export enum AcmeIdentifierType {
@@ -84,19 +83,12 @@ export const CreateAcmeAccountResponseSchema = z.object({
orders: z.string().optional()
});
export const ValidDNSIdentifierRegex = /^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.(?!-)[A-Za-z0-9-]{1,63}(?<!-))*$/;
// New Order payload schema
export const CreateAcmeOrderBodySchema = z.object({
identifiers: z.array(
z.object({
type: z.enum(Object.values(AcmeIdentifierType) as [string, ...string[]]),
value: z.string().refine((val) => {
// DNS label pattern: 1-63 chars, alphanumeric or hyphen, but not starting or ending with hyphen
const labelPattern = new RE2(/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/);
const labels = val.split(".");
return labels.every((label) => label.length >= 1 && label.length <= 63 && labelPattern.test(label));
}, "Invalid DNS identifier")
type: z.string(),
value: z.string()
})
),
notBefore: z.string().optional(),

View File

@@ -43,7 +43,7 @@ import {
AcmeServerInternalError,
AcmeUnsupportedIdentifierError
} from "./pki-acme-errors";
import { buildUrl, extractAccountIdFromKid } from "./pki-acme-fns";
import { buildUrl, extractAccountIdFromKid, validateDnsIdentifier } from "./pki-acme-fns";
import { TPkiAcmeOrderAuthDALFactory } from "./pki-acme-order-auth-dal";
import { TPkiAcmeOrderDALFactory } from "./pki-acme-order-dal";
import {
@@ -53,8 +53,7 @@ import {
AcmeIdentifierType,
AcmeOrderStatus,
CreateAcmeAccountBodySchema,
ProtectedHeaderSchema,
ValidDNSIdentifierRegex
ProtectedHeaderSchema
} from "./pki-acme-schemas";
import {
TAcmeOrderResource,
@@ -497,7 +496,7 @@ export const pkiAcmeServiceFactory = ({
if (
payload.identifiers.some(
(identifier) =>
!ValidDNSIdentifierRegex.test(identifier.value) ||
!validateDnsIdentifier(identifier.value) ||
isPrivateIp(identifier.value) ||
(!getConfig().isDevelopmentMode && identifier.value.toLowerCase() === "localhost")
)