s3 image file validation
This commit is contained in:
102
backend/utils/s3KeyValidator.js
Normal file
102
backend/utils/s3KeyValidator.js
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* S3 Key Validation Utility
|
||||
* Validates that user-supplied S3 keys match expected formats
|
||||
* to prevent IDOR and arbitrary data injection attacks
|
||||
*/
|
||||
|
||||
// UUID v4 regex pattern
|
||||
const UUID_PATTERN =
|
||||
"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}";
|
||||
|
||||
// Allowed image extensions
|
||||
const ALLOWED_EXTENSIONS = ["jpg", "jpeg", "png", "gif", "webp"];
|
||||
|
||||
// Valid S3 folders
|
||||
const VALID_FOLDERS = [
|
||||
"profiles",
|
||||
"items",
|
||||
"messages",
|
||||
"forum",
|
||||
"condition-checks",
|
||||
];
|
||||
|
||||
/**
|
||||
* Build regex pattern for a specific folder
|
||||
* @param {string} folder - The S3 folder name
|
||||
* @returns {RegExp}
|
||||
*/
|
||||
function buildKeyPattern(folder) {
|
||||
const extPattern = ALLOWED_EXTENSIONS.join("|");
|
||||
return new RegExp(`^${folder}/${UUID_PATTERN}\\.(${extPattern})$`, "i");
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a single S3 key
|
||||
* @param {string} key - The S3 key to validate
|
||||
* @param {string} folder - Expected folder (profiles, items, messages, forum, condition-checks)
|
||||
* @returns {{ valid: boolean, error?: string }}
|
||||
*/
|
||||
function validateS3Key(key, folder) {
|
||||
if (!key || typeof key !== "string") {
|
||||
return { valid: false, error: "Key must be a non-empty string" };
|
||||
}
|
||||
|
||||
if (!VALID_FOLDERS.includes(folder)) {
|
||||
return { valid: false, error: `Invalid folder` };
|
||||
}
|
||||
|
||||
const pattern = buildKeyPattern(folder);
|
||||
if (!pattern.test(key)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Invalid key format`,
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an array of S3 keys
|
||||
* @param {Array} keys - Array of S3 keys to validate
|
||||
* @param {string} folder - Expected folder (profiles, items, messages, forum, condition-checks)
|
||||
* @param {Object} options - Validation options
|
||||
* @param {number} options.maxKeys - Maximum number of keys allowed
|
||||
* @returns {{ valid: boolean, error?: string, invalidKeys?: Array }}
|
||||
*/
|
||||
function validateS3Keys(keys, folder, options = {}) {
|
||||
const { maxKeys = 20 } = options;
|
||||
|
||||
if (!Array.isArray(keys)) {
|
||||
return { valid: false, error: "Keys must be an array" };
|
||||
}
|
||||
|
||||
if (keys.length > maxKeys) {
|
||||
return { valid: false, error: `Maximum ${maxKeys} keys allowed` };
|
||||
}
|
||||
|
||||
if (keys.length === 0) {
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
const uniqueKeys = new Set(keys);
|
||||
if (uniqueKeys.size !== keys.length) {
|
||||
return { valid: false, error: "Duplicate keys not allowed" };
|
||||
}
|
||||
|
||||
const invalidKeys = [];
|
||||
for (const key of keys) {
|
||||
const result = validateS3Key(key, folder);
|
||||
if (!result.valid) {
|
||||
invalidKeys.push({ key, error: result.error });
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidKeys.length > 0) {
|
||||
return { valid: false, error: "Invalid S3 key format", invalidKeys };
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
module.exports = { validateS3Keys };
|
||||
Reference in New Issue
Block a user