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 106 107 108 109 110 111 112 113 114 115 116 117 118 | 170x 170x 170x 89x 38x 51x 51x 3x 48x 24x 2x 22x 170x 43x 43x 43x | /**
* 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,
},
tutor: {
maxCredentialChars: 512,
maxAnswerChars: 2_000,
maxFeedbackChars: 1_000,
maxHistoryEntries: 8,
},
examples: {
maxRepositoryOwnerChars: 39,
maxRepositoryNameChars: 100,
maxRepositorySlugChars: 140,
maxRefChars: 128,
fullCommitShaChars: 40,
},
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 {
Iif (!HEADER_NAME_PATTERN.test(name)) return false;
const basename = name.split(".")[0]?.toUpperCase();
return !WINDOWS_RESERVED_BASENAMES.has(basename);
}
|