Files
dockhand-upstream/src/lib/utils/env-name.ts
T
2026-06-15 14:56:51 +02:00

52 lines
1.9 KiB
TypeScript

/**
* Environment-name validation (#1179).
*
* Environment names end up in filesystem paths (e.g. $DATA_DIR/stacks/<name>/...
* in stacks.ts:344) and occasionally in shell-formatted strings, so the name
* has to be quote-safe and glob-free.
*
* The rule is permissive on purpose — strict alphanumeric would orphan
* existing real-world env names like "rambo (ARM)" or "docker-websites".
* Anything currently in the wild passes; only NEW characters that actively
* cause breakage are rejected.
*/
// Allowed characters: ASCII letters/digits + _ . - ( ) space + @
// First char: any allowed except space (no leading whitespace).
// Last char: any allowed except space and dot (no trailing whitespace,
// no trailing dot to avoid Windows-style "hidden" trailing-dot issues).
// Length: 1..64.
export const ENV_NAME_RE = /^(?! )[A-Za-z0-9_.\-() +@](?:[A-Za-z0-9_.\-() +@]{0,62}[A-Za-z0-9_\-)+@])?$/;
export const ENV_NAME_MAX_LENGTH = 64;
export interface ValidationResult {
ok: boolean;
reason?: string;
}
/**
* Validate an environment name. Returns a structured result so callers can
* decide how to surface the error (server: 400 body, client: inline message).
*/
export function validateEnvName(name: unknown): ValidationResult {
if (typeof name !== 'string') return { ok: false, reason: 'Name is required' };
if (name.length === 0) return { ok: false, reason: 'Name is required' };
if (name.length > ENV_NAME_MAX_LENGTH) {
return { ok: false, reason: `Name must be ${ENV_NAME_MAX_LENGTH} characters or fewer` };
}
if (!ENV_NAME_RE.test(name)) {
return {
ok: false,
reason:
'Name may contain letters, digits, spaces, and any of - _ . ( ) + @ (no leading/trailing whitespace, no trailing dot, no slashes, no wildcards)'
};
}
return { ok: true };
}
/** Convenience boolean variant. */
export function isValidEnvName(name: unknown): boolean {
return validateEnvName(name).ok;
}