Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | 31x 31x 31x 64x 34x 30x 30x 3x 27x 19x 2x 17x 31x 28x 27x 28x | /**
* Authoritative limits for data crossing the REST and WebSocket boundary.
*
* Keep this module dependency-free so client and server schemas share one
* contract. Values are intentionally expressed in bytes where payload size is
* relevant; JavaScript string limits are measured in UTF-16 code units by Zod.
*/
export const INPUT_LIMITS = {
rest: {
maxBodyBytes: 1024 * 1024,
},
compile: {
maxCodeChars: 128 * 1024,
maxHeaders: 20,
maxHeaderNameChars: 128,
maxHeaderContentChars: 32 * 1024,
maxLibraries: 20,
maxLibraryNameChars: 128,
maxFqbnChars: 128,
},
webSocket: {
maxPayloadBytes: 256 * 1024,
maxSerialInputChars: 4 * 1024,
maxTestRunIdChars: 64,
},
simulation: {
minTimeoutSeconds: 1,
defaultTimeoutSeconds: 60,
maxTimeoutSeconds: 300,
minPin: 0,
maxPin: 19,
// Arduino UNO ADC readings use the full 10-bit range. Digital writes use
// only 0/1, but this shared WebSocket command also carries analog inputs.
maxPinValue: 1023,
minBaudrate: 300,
maxBaudrate: 115_200,
},
} as const;
/** URL-safe identifier accepted for test-only artifact namespaces. */
export const TEST_RUN_ID_PATTERN = new RegExp(
`^[A-Za-z0-9_-]{1,${INPUT_LIMITS.webSocket.maxTestRunIdChars}}$`,
);
/** Portable header basename; separators and traversal tokens cannot match. */
export const HEADER_NAME_PATTERN = new RegExp(
`^[A-Za-z0-9][A-Za-z0-9_.-]{0,${INPUT_LIMITS.compile.maxHeaderNameChars - 1}}$`,
);
export function normalizeSimulationTimeout(timeoutSeconds: number | undefined): number {
if (
timeoutSeconds === undefined ||
!Number.isFinite(timeoutSeconds)
) {
return INPUT_LIMITS.simulation.defaultTimeoutSeconds;
}
const roundedTimeout = Math.floor(timeoutSeconds);
if (roundedTimeout < INPUT_LIMITS.simulation.minTimeoutSeconds) {
return INPUT_LIMITS.simulation.defaultTimeoutSeconds;
}
return Math.min(roundedTimeout, INPUT_LIMITS.simulation.maxTimeoutSeconds);
}
export function normalizeBaudrate(baudrate: number): number {
if (
!Number.isInteger(baudrate) ||
baudrate < INPUT_LIMITS.simulation.minBaudrate ||
baudrate > INPUT_LIMITS.simulation.maxBaudrate
) {
return 9600;
}
return baudrate;
}
const WINDOWS_RESERVED_BASENAMES = new Set([
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9",
]);
export function isSafeHeaderName(name: string): boolean {
if (!HEADER_NAME_PATTERN.test(name)) return false;
const basename = name.split(".")[0]?.toUpperCase();
return !WINDOWS_RESERVED_BASENAMES.has(basename);
}
|