Use re2 for identifier validation

This commit is contained in:
Fang-Pen Lin
2025-11-12 09:12:32 -08:00
parent 97b7e6c6bf
commit e5168e88e4
3 changed files with 14 additions and 14 deletions
@@ -1,3 +1,5 @@
import { getConfig } from "@app/lib/config/env";
import RE2 from "re2";
import { z } from "zod"; import { z } from "zod";
import { getConfig } from "@app/lib/config/env"; 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)); 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));
};
@@ -1,4 +1,3 @@
import RE2 from "re2";
import { z } from "zod"; import { z } from "zod";
export enum AcmeIdentifierType { export enum AcmeIdentifierType {
@@ -84,19 +83,12 @@ export const CreateAcmeAccountResponseSchema = z.object({
orders: z.string().optional() orders: z.string().optional()
}); });
export const ValidDNSIdentifierRegex = /^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.(?!-)[A-Za-z0-9-]{1,63}(?<!-))*$/;
// New Order payload schema // New Order payload schema
export const CreateAcmeOrderBodySchema = z.object({ export const CreateAcmeOrderBodySchema = z.object({
identifiers: z.array( identifiers: z.array(
z.object({ z.object({
type: z.enum(Object.values(AcmeIdentifierType) as [string, ...string[]]), type: z.string(),
value: z.string().refine((val) => { value: z.string()
// 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")
}) })
), ),
notBefore: z.string().optional(), notBefore: z.string().optional(),
@@ -43,7 +43,7 @@ import {
AcmeServerInternalError, AcmeServerInternalError,
AcmeUnsupportedIdentifierError AcmeUnsupportedIdentifierError
} from "./pki-acme-errors"; } 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 { TPkiAcmeOrderAuthDALFactory } from "./pki-acme-order-auth-dal";
import { TPkiAcmeOrderDALFactory } from "./pki-acme-order-dal"; import { TPkiAcmeOrderDALFactory } from "./pki-acme-order-dal";
import { import {
@@ -53,8 +53,7 @@ import {
AcmeIdentifierType, AcmeIdentifierType,
AcmeOrderStatus, AcmeOrderStatus,
CreateAcmeAccountBodySchema, CreateAcmeAccountBodySchema,
ProtectedHeaderSchema, ProtectedHeaderSchema
ValidDNSIdentifierRegex
} from "./pki-acme-schemas"; } from "./pki-acme-schemas";
import { import {
TAcmeOrderResource, TAcmeOrderResource,
@@ -497,7 +496,7 @@ export const pkiAcmeServiceFactory = ({
if ( if (
payload.identifiers.some( payload.identifiers.some(
(identifier) => (identifier) =>
!ValidDNSIdentifierRegex.test(identifier.value) || !validateDnsIdentifier(identifier.value) ||
isPrivateIp(identifier.value) || isPrivateIp(identifier.value) ||
(!getConfig().isDevelopmentMode && identifier.value.toLowerCase() === "localhost") (!getConfig().isDevelopmentMode && identifier.value.toLowerCase() === "localhost")
) )