payload 3.89.0 → 3.90.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/forms/Field.d.ts +2 -0
- package/dist/admin/forms/Field.d.ts.map +1 -1
- package/dist/admin/forms/Field.js.map +1 -1
- package/dist/admin/forms/Form.d.ts +2 -0
- package/dist/admin/forms/Form.d.ts.map +1 -1
- package/dist/admin/forms/Form.js.map +1 -1
- package/dist/admin/functions/index.d.ts +3 -1
- package/dist/admin/functions/index.d.ts.map +1 -1
- package/dist/admin/functions/index.js.map +1 -1
- package/dist/admin/views/dashboard.d.ts +1 -1
- package/dist/admin/views/dashboard.d.ts.map +1 -1
- package/dist/admin/views/dashboard.js.map +1 -1
- package/dist/auth/applyUserReadAccess.d.ts +15 -0
- package/dist/auth/applyUserReadAccess.d.ts.map +1 -0
- package/dist/auth/applyUserReadAccess.js +41 -0
- package/dist/auth/applyUserReadAccess.js.map +1 -0
- package/dist/auth/baseFields/apiKey/canCreateOrUpdateAPIKey.d.ts +4 -0
- package/dist/auth/baseFields/apiKey/canCreateOrUpdateAPIKey.d.ts.map +1 -0
- package/dist/auth/baseFields/apiKey/canCreateOrUpdateAPIKey.js +20 -0
- package/dist/auth/baseFields/apiKey/canCreateOrUpdateAPIKey.js.map +1 -0
- package/dist/auth/baseFields/apiKey/encryptAPIKey.d.ts +4 -0
- package/dist/auth/baseFields/apiKey/encryptAPIKey.d.ts.map +1 -0
- package/dist/auth/baseFields/apiKey/encryptAPIKey.js +16 -0
- package/dist/auth/baseFields/apiKey/encryptAPIKey.js.map +1 -0
- package/dist/auth/baseFields/apiKey/index.d.ts +6 -0
- package/dist/auth/baseFields/apiKey/index.d.ts.map +1 -0
- package/dist/auth/baseFields/apiKey/index.js +112 -0
- package/dist/auth/baseFields/apiKey/index.js.map +1 -0
- package/dist/auth/baseFields/apiKey/omitAPIKey.d.ts +4 -0
- package/dist/auth/baseFields/apiKey/omitAPIKey.d.ts.map +1 -0
- package/dist/auth/baseFields/apiKey/omitAPIKey.js +8 -0
- package/dist/auth/baseFields/apiKey/omitAPIKey.js.map +1 -0
- package/dist/auth/baseFields/apiKey/omitAPIKeyIndex.d.ts +4 -0
- package/dist/auth/baseFields/apiKey/omitAPIKeyIndex.d.ts.map +1 -0
- package/dist/auth/baseFields/apiKey/omitAPIKeyIndex.js +5 -0
- package/dist/auth/baseFields/apiKey/omitAPIKeyIndex.js.map +1 -0
- package/dist/auth/baseFields/apiKey/omitEncryptedAPIKey.d.ts +4 -0
- package/dist/auth/baseFields/apiKey/omitEncryptedAPIKey.d.ts.map +1 -0
- package/dist/auth/baseFields/apiKey/omitEncryptedAPIKey.js +5 -0
- package/dist/auth/baseFields/apiKey/omitEncryptedAPIKey.js.map +1 -0
- package/dist/auth/baseFields/apiKey/setAPIKeyIndex.d.ts +4 -0
- package/dist/auth/baseFields/apiKey/setAPIKeyIndex.d.ts.map +1 -0
- package/dist/auth/baseFields/apiKey/setAPIKeyIndex.js +17 -0
- package/dist/auth/baseFields/apiKey/setAPIKeyIndex.js.map +1 -0
- package/dist/auth/baseFields/auth.d.ts +1 -0
- package/dist/auth/baseFields/auth.d.ts.map +1 -1
- package/dist/auth/baseFields/auth.js +9 -0
- package/dist/auth/baseFields/auth.js.map +1 -1
- package/dist/auth/defaultUnlockAccess.d.ts +3 -0
- package/dist/auth/defaultUnlockAccess.d.ts.map +1 -0
- package/dist/auth/defaultUnlockAccess.js +8 -0
- package/dist/auth/defaultUnlockAccess.js.map +1 -0
- package/dist/auth/endpoints/index.d.ts +1 -0
- package/dist/auth/endpoints/index.d.ts.map +1 -1
- package/dist/auth/endpoints/index.js +8 -0
- package/dist/auth/endpoints/index.js.map +1 -1
- package/dist/auth/endpoints/revealAPIKey.d.ts +7 -0
- package/dist/auth/endpoints/revealAPIKey.d.ts.map +1 -0
- package/dist/auth/endpoints/revealAPIKey.js +120 -0
- package/dist/auth/endpoints/revealAPIKey.js.map +1 -0
- package/dist/auth/executeAccess.d.ts +3 -1
- package/dist/auth/executeAccess.d.ts.map +1 -1
- package/dist/auth/executeAccess.js +2 -1
- package/dist/auth/executeAccess.js.map +1 -1
- package/dist/auth/getAuthFields.d.ts +1 -1
- package/dist/auth/getAuthFields.d.ts.map +1 -1
- package/dist/auth/getAuthFields.js +7 -4
- package/dist/auth/getAuthFields.js.map +1 -1
- package/dist/auth/getFieldsToSign.d.ts.map +1 -1
- package/dist/auth/getFieldsToSign.js +17 -10
- package/dist/auth/getFieldsToSign.js.map +1 -1
- package/dist/auth/getFieldsToSign.spec.js +114 -0
- package/dist/auth/getFieldsToSign.spec.js.map +1 -0
- package/dist/auth/jwt.d.ts.map +1 -1
- package/dist/auth/jwt.js +2 -0
- package/dist/auth/jwt.js.map +1 -1
- package/dist/auth/jwt.spec.js +24 -0
- package/dist/auth/jwt.spec.js.map +1 -0
- package/dist/auth/jwtAuth.d.ts +9 -0
- package/dist/auth/jwtAuth.d.ts.map +1 -0
- package/dist/auth/jwtAuth.js +8 -0
- package/dist/auth/jwtAuth.js.map +1 -0
- package/dist/auth/operations/auth.d.ts.map +1 -1
- package/dist/auth/operations/auth.js +2 -1
- package/dist/auth/operations/auth.js.map +1 -1
- package/dist/auth/operations/forgotPassword.d.ts.map +1 -1
- package/dist/auth/operations/forgotPassword.js +95 -7
- package/dist/auth/operations/forgotPassword.js.map +1 -1
- package/dist/auth/operations/local/auth.js +3 -1
- package/dist/auth/operations/local/auth.js.map +1 -1
- package/dist/auth/operations/login.d.ts.map +1 -1
- package/dist/auth/operations/login.js +48 -26
- package/dist/auth/operations/login.js.map +1 -1
- package/dist/auth/operations/refresh.d.ts.map +1 -1
- package/dist/auth/operations/refresh.js +32 -9
- package/dist/auth/operations/refresh.js.map +1 -1
- package/dist/auth/operations/registerFirstUser.d.ts.map +1 -1
- package/dist/auth/operations/registerFirstUser.js +3 -4
- package/dist/auth/operations/registerFirstUser.js.map +1 -1
- package/dist/auth/operations/resetPassword.d.ts.map +1 -1
- package/dist/auth/operations/resetPassword.js +31 -22
- package/dist/auth/operations/resetPassword.js.map +1 -1
- package/dist/auth/operations/unlock.d.ts.map +1 -1
- package/dist/auth/operations/unlock.js +1 -0
- package/dist/auth/operations/unlock.js.map +1 -1
- package/dist/auth/strategies/apiKey.d.ts.map +1 -1
- package/dist/auth/strategies/apiKey.js +15 -1
- package/dist/auth/strategies/apiKey.js.map +1 -1
- package/dist/auth/strategies/jwt.d.ts.map +1 -1
- package/dist/auth/strategies/jwt.js +61 -10
- package/dist/auth/strategies/jwt.js.map +1 -1
- package/dist/auth/strategies/jwt.spec.js +281 -0
- package/dist/auth/strategies/jwt.spec.js.map +1 -0
- package/dist/auth/strategies/local/authenticate.d.ts +5 -1
- package/dist/auth/strategies/local/authenticate.d.ts.map +1 -1
- package/dist/auth/strategies/local/authenticate.js +8 -3
- package/dist/auth/strategies/local/authenticate.js.map +1 -1
- package/dist/auth/strategies/local/authenticate.spec.js +37 -10
- package/dist/auth/strategies/local/authenticate.spec.js.map +1 -1
- package/dist/auth/strategies/local/generatePasswordSaltHash.d.ts +9 -1
- package/dist/auth/strategies/local/generatePasswordSaltHash.d.ts.map +1 -1
- package/dist/auth/strategies/local/generatePasswordSaltHash.js +61 -34
- package/dist/auth/strategies/local/generatePasswordSaltHash.js.map +1 -1
- package/dist/auth/types.d.ts +16 -1
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth/types.js.map +1 -1
- package/dist/collections/config/client.d.ts +1 -0
- package/dist/collections/config/client.d.ts.map +1 -1
- package/dist/collections/config/client.js +3 -0
- package/dist/collections/config/client.js.map +1 -1
- package/dist/collections/config/defaults.d.ts.map +1 -1
- package/dist/collections/config/defaults.js +4 -2
- package/dist/collections/config/defaults.js.map +1 -1
- package/dist/collections/config/sanitize.d.ts.map +1 -1
- package/dist/collections/config/sanitize.js +17 -6
- package/dist/collections/config/sanitize.js.map +1 -1
- package/dist/collections/endpoints/index.d.ts +1 -0
- package/dist/collections/endpoints/index.d.ts.map +1 -1
- package/dist/collections/endpoints/index.js +6 -5
- package/dist/collections/endpoints/index.js.map +1 -1
- package/dist/collections/operations/create.d.ts.map +1 -1
- package/dist/collections/operations/create.js +105 -8
- package/dist/collections/operations/create.js.map +1 -1
- package/dist/collections/operations/deleteByID.js +13 -13
- package/dist/collections/operations/deleteByID.js.map +1 -1
- package/dist/collections/operations/findDistinct.d.ts.map +1 -1
- package/dist/collections/operations/findDistinct.js +96 -18
- package/dist/collections/operations/findDistinct.js.map +1 -1
- package/dist/collections/operations/restoreVersion.d.ts.map +1 -1
- package/dist/collections/operations/restoreVersion.js +47 -13
- package/dist/collections/operations/restoreVersion.js.map +1 -1
- package/dist/collections/operations/update.d.ts.map +1 -1
- package/dist/collections/operations/update.js +118 -18
- package/dist/collections/operations/update.js.map +1 -1
- package/dist/collections/operations/updateByID.d.ts.map +1 -1
- package/dist/collections/operations/updateByID.js +62 -2
- package/dist/collections/operations/updateByID.js.map +1 -1
- package/dist/collections/operations/utilities/update.d.ts.map +1 -1
- package/dist/collections/operations/utilities/update.js +71 -15
- package/dist/collections/operations/utilities/update.js.map +1 -1
- package/dist/config/orderable/index.d.ts.map +1 -1
- package/dist/config/orderable/index.js +67 -14
- package/dist/config/orderable/index.js.map +1 -1
- package/dist/config/sanitize.d.ts.map +1 -1
- package/dist/config/sanitize.js +1 -1
- package/dist/config/sanitize.js.map +1 -1
- package/dist/config/types.d.ts +13 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js.map +1 -1
- package/dist/database/getLocalizedPaths.d.ts +2 -1
- package/dist/database/getLocalizedPaths.d.ts.map +1 -1
- package/dist/database/getLocalizedPaths.js +12 -3
- package/dist/database/getLocalizedPaths.js.map +1 -1
- package/dist/database/isNestedRelationshipQuery.d.ts +9 -0
- package/dist/database/isNestedRelationshipQuery.d.ts.map +1 -0
- package/dist/database/isNestedRelationshipQuery.js +14 -0
- package/dist/database/isNestedRelationshipQuery.js.map +1 -0
- package/dist/database/prefixWherePaths.d.ts +6 -0
- package/dist/database/prefixWherePaths.d.ts.map +1 -0
- package/dist/database/prefixWherePaths.js +19 -0
- package/dist/database/prefixWherePaths.js.map +1 -0
- package/dist/database/prefixWherePaths.spec.js +46 -0
- package/dist/database/prefixWherePaths.spec.js.map +1 -0
- package/dist/database/queryValidation/validateQueryPaths.d.ts +2 -1
- package/dist/database/queryValidation/validateQueryPaths.d.ts.map +1 -1
- package/dist/database/queryValidation/validateQueryPaths.js +4 -1
- package/dist/database/queryValidation/validateQueryPaths.js.map +1 -1
- package/dist/database/queryValidation/validateSearchParams.d.ts +2 -1
- package/dist/database/queryValidation/validateSearchParams.d.ts.map +1 -1
- package/dist/database/queryValidation/validateSearchParams.js +87 -4
- package/dist/database/queryValidation/validateSearchParams.js.map +1 -1
- package/dist/database/types.d.ts +5 -0
- package/dist/database/types.d.ts.map +1 -1
- package/dist/database/types.js.map +1 -1
- package/dist/email/normalizeSendEmailOptions.js.map +1 -1
- package/dist/email/types.d.ts +4 -1
- package/dist/email/types.d.ts.map +1 -1
- package/dist/email/types.js.map +1 -1
- package/dist/exports/internal.d.ts +7 -0
- package/dist/exports/internal.d.ts.map +1 -1
- package/dist/exports/internal.js +7 -1
- package/dist/exports/internal.js.map +1 -1
- package/dist/exports/shared.d.ts +3 -0
- package/dist/exports/shared.d.ts.map +1 -1
- package/dist/exports/shared.js +3 -0
- package/dist/exports/shared.js.map +1 -1
- package/dist/fields/config/types.d.ts +7 -5
- package/dist/fields/config/types.d.ts.map +1 -1
- package/dist/fields/config/types.js.map +1 -1
- package/dist/fields/hooks/beforeChange/index.d.ts +7 -1
- package/dist/fields/hooks/beforeChange/index.d.ts.map +1 -1
- package/dist/fields/hooks/beforeChange/index.js +4 -2
- package/dist/fields/hooks/beforeChange/index.js.map +1 -1
- package/dist/fields/hooks/beforeChange/index.spec.js +206 -0
- package/dist/fields/hooks/beforeChange/index.spec.js.map +1 -0
- package/dist/fields/hooks/beforeChange/promise.d.ts +2 -1
- package/dist/fields/hooks/beforeChange/promise.d.ts.map +1 -1
- package/dist/fields/hooks/beforeChange/promise.js +51 -9
- package/dist/fields/hooks/beforeChange/promise.js.map +1 -1
- package/dist/fields/hooks/beforeChange/traverseFields.d.ts +2 -1
- package/dist/fields/hooks/beforeChange/traverseFields.d.ts.map +1 -1
- package/dist/fields/hooks/beforeChange/traverseFields.js +3 -2
- package/dist/fields/hooks/beforeChange/traverseFields.js.map +1 -1
- package/dist/fields/hooks/beforeDuplicate/promise.d.ts.map +1 -1
- package/dist/fields/hooks/beforeDuplicate/promise.js +4 -0
- package/dist/fields/hooks/beforeDuplicate/promise.js.map +1 -1
- package/dist/fields/hooks/beforeValidate/getFallbackValue.d.ts +2 -1
- package/dist/fields/hooks/beforeValidate/getFallbackValue.d.ts.map +1 -1
- package/dist/fields/hooks/beforeValidate/getFallbackValue.js +2 -2
- package/dist/fields/hooks/beforeValidate/getFallbackValue.js.map +1 -1
- package/dist/fields/hooks/beforeValidate/index.d.ts +6 -1
- package/dist/fields/hooks/beforeValidate/index.d.ts.map +1 -1
- package/dist/fields/hooks/beforeValidate/index.js +3 -1
- package/dist/fields/hooks/beforeValidate/index.js.map +1 -1
- package/dist/fields/hooks/beforeValidate/index.spec.js +90 -0
- package/dist/fields/hooks/beforeValidate/index.spec.js.map +1 -0
- package/dist/fields/hooks/beforeValidate/promise.d.ts +6 -1
- package/dist/fields/hooks/beforeValidate/promise.d.ts.map +1 -1
- package/dist/fields/hooks/beforeValidate/promise.js +23 -8
- package/dist/fields/hooks/beforeValidate/promise.js.map +1 -1
- package/dist/fields/hooks/beforeValidate/traverseFields.d.ts +6 -1
- package/dist/fields/hooks/beforeValidate/traverseFields.d.ts.map +1 -1
- package/dist/fields/hooks/beforeValidate/traverseFields.js +3 -1
- package/dist/fields/hooks/beforeValidate/traverseFields.js.map +1 -1
- package/dist/fields/validations.d.ts.map +1 -1
- package/dist/fields/validations.js +3 -1
- package/dist/fields/validations.js.map +1 -1
- package/dist/globals/operations/restoreVersion.d.ts.map +1 -1
- package/dist/globals/operations/restoreVersion.js +45 -11
- package/dist/globals/operations/restoreVersion.js.map +1 -1
- package/dist/globals/operations/update.d.ts.map +1 -1
- package/dist/globals/operations/update.js +95 -16
- package/dist/globals/operations/update.js.map +1 -1
- package/dist/index.bundled.d.ts +102 -24
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/preferences/config.d.ts.map +1 -1
- package/dist/preferences/config.js +2 -1
- package/dist/preferences/config.js.map +1 -1
- package/dist/query-presets/access.d.ts.map +1 -1
- package/dist/query-presets/access.js +2 -1
- package/dist/query-presets/access.js.map +1 -1
- package/dist/queues/config/types/index.d.ts +2 -1
- package/dist/queues/config/types/index.d.ts.map +1 -1
- package/dist/queues/config/types/index.js.map +1 -1
- package/dist/uploads/assertClientUploadAccess.d.ts +7 -0
- package/dist/uploads/assertClientUploadAccess.d.ts.map +1 -0
- package/dist/uploads/assertClientUploadAccess.js +27 -0
- package/dist/uploads/assertClientUploadAccess.js.map +1 -0
- package/dist/uploads/assertClientUploadAllowed.d.ts +11 -0
- package/dist/uploads/assertClientUploadAllowed.d.ts.map +1 -0
- package/dist/uploads/assertClientUploadAllowed.js +46 -0
- package/dist/uploads/assertClientUploadAllowed.js.map +1 -0
- package/dist/uploads/assertClientUploadAllowed.spec.js +91 -0
- package/dist/uploads/assertClientUploadAllowed.spec.js.map +1 -0
- package/dist/uploads/checkFileRestrictions.d.ts +4 -1
- package/dist/uploads/checkFileRestrictions.d.ts.map +1 -1
- package/dist/uploads/checkFileRestrictions.js +66 -26
- package/dist/uploads/checkFileRestrictions.js.map +1 -1
- package/dist/uploads/clientUploadReceipt.d.ts +23 -0
- package/dist/uploads/clientUploadReceipt.d.ts.map +1 -0
- package/dist/uploads/clientUploadReceipt.js +55 -0
- package/dist/uploads/clientUploadReceipt.js.map +1 -0
- package/dist/uploads/deleteAssociatedFiles.d.ts.map +1 -1
- package/dist/uploads/deleteAssociatedFiles.js +33 -8
- package/dist/uploads/deleteAssociatedFiles.js.map +1 -1
- package/dist/uploads/deleteAssociatedFiles.spec.js +124 -0
- package/dist/uploads/deleteAssociatedFiles.spec.js.map +1 -0
- package/dist/uploads/detectSvgFromXml.d.ts +1 -3
- package/dist/uploads/detectSvgFromXml.d.ts.map +1 -1
- package/dist/uploads/detectSvgFromXml.js +3 -40
- package/dist/uploads/detectSvgFromXml.js.map +1 -1
- package/dist/uploads/endpoints/getFile.d.ts.map +1 -1
- package/dist/uploads/endpoints/getFile.js +5 -3
- package/dist/uploads/endpoints/getFile.js.map +1 -1
- package/dist/uploads/fetchAPI-multipart/handlers.d.ts +2 -2
- package/dist/uploads/fetchAPI-multipart/handlers.d.ts.map +1 -1
- package/dist/uploads/fetchAPI-multipart/handlers.js +18 -5
- package/dist/uploads/fetchAPI-multipart/handlers.js.map +1 -1
- package/dist/uploads/fetchAPI-multipart/index.d.ts.map +1 -1
- package/dist/uploads/fetchAPI-multipart/index.js +24 -2
- package/dist/uploads/fetchAPI-multipart/index.js.map +1 -1
- package/dist/uploads/fetchAPI-multipart/processMultipart.d.ts +0 -6
- package/dist/uploads/fetchAPI-multipart/processMultipart.d.ts.map +1 -1
- package/dist/uploads/fetchAPI-multipart/processMultipart.js +146 -126
- package/dist/uploads/fetchAPI-multipart/processMultipart.js.map +1 -1
- package/dist/uploads/fetchAPI-multipart/processMultipart.spec.js +663 -0
- package/dist/uploads/fetchAPI-multipart/processMultipart.spec.js.map +1 -0
- package/dist/uploads/generateFileData.d.ts +3 -1
- package/dist/uploads/generateFileData.d.ts.map +1 -1
- package/dist/uploads/generateFileData.js +29 -16
- package/dist/uploads/generateFileData.js.map +1 -1
- package/dist/uploads/getBaseFields.d.ts.map +1 -1
- package/dist/uploads/getBaseFields.js +3 -1
- package/dist/uploads/getBaseFields.js.map +1 -1
- package/dist/uploads/getExternalFile.d.ts +1 -1
- package/dist/uploads/getExternalFile.d.ts.map +1 -1
- package/dist/uploads/getExternalFile.js +60 -15
- package/dist/uploads/getExternalFile.js.map +1 -1
- package/dist/uploads/getFileTypeFallback.d.ts.map +1 -1
- package/dist/uploads/getFileTypeFallback.js +3 -1
- package/dist/uploads/getFileTypeFallback.js.map +1 -1
- package/dist/uploads/getFileTypeIdentity.d.ts +19 -0
- package/dist/uploads/getFileTypeIdentity.d.ts.map +1 -0
- package/dist/uploads/getFileTypeIdentity.js +34 -0
- package/dist/uploads/getFileTypeIdentity.js.map +1 -0
- package/dist/uploads/getFileTypeIdentity.spec.js +43 -0
- package/dist/uploads/getFileTypeIdentity.spec.js.map +1 -0
- package/dist/uploads/isProcessableImage.d.ts +2 -0
- package/dist/uploads/isProcessableImage.d.ts.map +1 -0
- package/dist/uploads/isProcessableImage.js +7 -0
- package/dist/uploads/isProcessableImage.js.map +1 -0
- package/dist/uploads/safeFetch.d.ts.map +1 -1
- package/dist/uploads/safeFetch.js +3 -2
- package/dist/uploads/safeFetch.js.map +1 -1
- package/dist/uploads/sanitizeUploadData.d.ts +26 -0
- package/dist/uploads/sanitizeUploadData.d.ts.map +1 -0
- package/dist/uploads/sanitizeUploadData.js +159 -0
- package/dist/uploads/sanitizeUploadData.js.map +1 -0
- package/dist/uploads/sanitizeUploadData.spec.js +255 -0
- package/dist/uploads/sanitizeUploadData.spec.js.map +1 -0
- package/dist/uploads/types.d.ts +11 -3
- package/dist/uploads/types.d.ts.map +1 -1
- package/dist/uploads/types.js.map +1 -1
- package/dist/uploads/uploadContentSecurityPolicy.d.ts +2 -0
- package/dist/uploads/uploadContentSecurityPolicy.d.ts.map +1 -0
- package/dist/uploads/uploadContentSecurityPolicy.js +3 -0
- package/dist/uploads/uploadContentSecurityPolicy.js.map +1 -0
- package/dist/uploads/validateISOBaseMediaFile.d.ts +4 -0
- package/dist/uploads/validateISOBaseMediaFile.d.ts.map +1 -0
- package/dist/uploads/validateISOBaseMediaFile.js +91 -0
- package/dist/uploads/validateISOBaseMediaFile.js.map +1 -0
- package/dist/uploads/validateISOBaseMediaFile.spec.js +130 -0
- package/dist/uploads/validateISOBaseMediaFile.spec.js.map +1 -0
- package/dist/uploads/validateSvg.d.ts +10 -4
- package/dist/uploads/validateSvg.d.ts.map +1 -1
- package/dist/uploads/validateSvg.js +280 -40
- package/dist/uploads/validateSvg.js.map +1 -1
- package/dist/uploads/validateSvg.spec.js +123 -0
- package/dist/uploads/validateSvg.spec.js.map +1 -0
- package/dist/uploads/validateUploadFilename.d.ts +3 -0
- package/dist/uploads/validateUploadFilename.d.ts.map +1 -0
- package/dist/uploads/validateUploadFilename.js +13 -0
- package/dist/uploads/validateUploadFilename.js.map +1 -0
- package/dist/uploads/validateUploadFilename.spec.js +24 -0
- package/dist/uploads/validateUploadFilename.spec.js.map +1 -0
- package/dist/utilities/addDataAndFileToRequest.d.ts.map +1 -1
- package/dist/utilities/addDataAndFileToRequest.js +28 -1
- package/dist/utilities/addDataAndFileToRequest.js.map +1 -1
- package/dist/utilities/addDataAndFileToRequest.spec.js +158 -0
- package/dist/utilities/addDataAndFileToRequest.spec.js.map +1 -1
- package/dist/utilities/createPayloadRequest.d.ts.map +1 -1
- package/dist/utilities/createPayloadRequest.js +2 -1
- package/dist/utilities/createPayloadRequest.js.map +1 -1
- package/dist/utilities/deepCopyObject.d.ts.map +1 -1
- package/dist/utilities/deepCopyObject.js +110 -19
- package/dist/utilities/deepCopyObject.js.map +1 -1
- package/dist/utilities/deepCopyObject.spec.js +216 -0
- package/dist/utilities/deepCopyObject.spec.js.map +1 -0
- package/dist/utilities/fieldPath.d.ts +13 -0
- package/dist/utilities/fieldPath.d.ts.map +1 -0
- package/dist/utilities/fieldPath.js +51 -0
- package/dist/utilities/fieldPath.js.map +1 -0
- package/dist/utilities/fieldPath.spec.js +155 -0
- package/dist/utilities/fieldPath.spec.js.map +1 -0
- package/dist/utilities/getEntityPermissions/getEntityPermissions.d.ts.map +1 -1
- package/dist/utilities/getEntityPermissions/getEntityPermissions.js +2 -0
- package/dist/utilities/getEntityPermissions/getEntityPermissions.js.map +1 -1
- package/dist/utilities/getObjectDotNotation.d.ts.map +1 -1
- package/dist/utilities/getObjectDotNotation.js +7 -1
- package/dist/utilities/getObjectDotNotation.js.map +1 -1
- package/dist/utilities/getObjectDotNotation.spec.js +33 -0
- package/dist/utilities/getObjectDotNotation.spec.js.map +1 -0
- package/dist/utilities/getSafeRedirect.spec.js +14 -0
- package/dist/utilities/getSafeRedirect.spec.js.map +1 -1
- package/dist/utilities/getTopLevelFieldNames.d.ts +2 -0
- package/dist/utilities/getTopLevelFieldNames.d.ts.map +1 -0
- package/dist/utilities/getTopLevelFieldNames.js +3 -0
- package/dist/utilities/getTopLevelFieldNames.js.map +1 -0
- package/dist/utilities/getTopLevelFieldNames.spec.js +18 -0
- package/dist/utilities/getTopLevelFieldNames.spec.js.map +1 -0
- package/dist/utilities/handleEndpoints.js +1 -1
- package/dist/utilities/handleEndpoints.js.map +1 -1
- package/dist/utilities/parseParams/index.d.ts.map +1 -1
- package/dist/utilities/parseParams/index.js +3 -1
- package/dist/utilities/parseParams/index.js.map +1 -1
- package/dist/utilities/parseParams/index.spec.js +8 -0
- package/dist/utilities/parseParams/index.spec.js.map +1 -1
- package/dist/utilities/telemetry/featureInfo/getFeatureInfo.d.ts +4 -0
- package/dist/utilities/telemetry/featureInfo/getFeatureInfo.d.ts.map +1 -0
- package/dist/utilities/telemetry/featureInfo/getFeatureInfo.js +248 -0
- package/dist/utilities/telemetry/featureInfo/getFeatureInfo.js.map +1 -0
- package/dist/utilities/telemetry/featureInfo/types.d.ts +62 -0
- package/dist/utilities/telemetry/featureInfo/types.d.ts.map +1 -0
- package/dist/utilities/telemetry/featureInfo/types.js +3 -0
- package/dist/utilities/telemetry/featureInfo/types.js.map +1 -0
- package/dist/utilities/telemetry/getProjectContext.d.ts +14 -0
- package/dist/utilities/telemetry/getProjectContext.d.ts.map +1 -0
- package/dist/utilities/telemetry/getProjectContext.js +36 -0
- package/dist/utilities/telemetry/getProjectContext.js.map +1 -0
- package/dist/utilities/telemetry/index.d.ts +7 -1
- package/dist/utilities/telemetry/index.d.ts.map +1 -1
- package/dist/utilities/telemetry/index.js +22 -0
- package/dist/utilities/telemetry/index.js.map +1 -1
- package/dist/utilities/traverseFields.d.ts.map +1 -1
- package/dist/utilities/traverseFields.js +5 -14
- package/dist/utilities/traverseFields.js.map +1 -1
- package/dist/utilities/unflatten.d.ts.map +1 -1
- package/dist/utilities/unflatten.js +21 -9
- package/dist/utilities/unflatten.js.map +1 -1
- package/dist/utilities/unflatten.spec.js +38 -0
- package/dist/utilities/unflatten.spec.js.map +1 -0
- package/dist/versions/allLocalesPublicationStatus.d.ts +38 -0
- package/dist/versions/allLocalesPublicationStatus.d.ts.map +1 -0
- package/dist/versions/allLocalesPublicationStatus.js +57 -0
- package/dist/versions/allLocalesPublicationStatus.js.map +1 -0
- package/dist/versions/getLatestGlobalVersion.d.ts.map +1 -1
- package/dist/versions/getLatestGlobalVersion.js +10 -2
- package/dist/versions/getLatestGlobalVersion.js.map +1 -1
- package/dist/versions/getRestoredStatusesToAuthorize.d.ts +9 -0
- package/dist/versions/getRestoredStatusesToAuthorize.d.ts.map +1 -0
- package/dist/versions/getRestoredStatusesToAuthorize.js +22 -0
- package/dist/versions/getRestoredStatusesToAuthorize.js.map +1 -0
- package/dist/versions/getRestoredStatusesToAuthorize.spec.js +50 -0
- package/dist/versions/getRestoredStatusesToAuthorize.spec.js.map +1 -0
- package/dist/versions/schedule/job.d.ts +2 -2
- package/dist/versions/schedule/job.d.ts.map +1 -1
- package/dist/versions/schedule/job.js +11 -7
- package/dist/versions/schedule/job.js.map +1 -1
- package/dist/versions/schedule/types.d.ts +5 -1
- package/dist/versions/schedule/types.d.ts.map +1 -1
- package/dist/versions/schedule/types.js.map +1 -1
- package/package.json +4 -2
- package/dist/auth/baseFields/apiKey.d.ts +0 -3
- package/dist/auth/baseFields/apiKey.d.ts.map +0 -1
- package/dist/auth/baseFields/apiKey.js +0 -59
- package/dist/auth/baseFields/apiKey.js.map +0 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { fileTypeFromBuffer } from 'file-type';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
7
|
+
import { isISOBaseMediaMimeType, validateISOBaseMediaBuffer, validateISOBaseMediaFile } from './validateISOBaseMediaFile.js';
|
|
8
|
+
const tempFiles = [];
|
|
9
|
+
const box = (type, content = Buffer.alloc(0), size = content.length + 8)=>{
|
|
10
|
+
const header = Buffer.alloc(8);
|
|
11
|
+
header.writeUInt32BE(size);
|
|
12
|
+
header.write(type, 4, 4, 'latin1');
|
|
13
|
+
return Buffer.concat([
|
|
14
|
+
header,
|
|
15
|
+
content
|
|
16
|
+
]);
|
|
17
|
+
};
|
|
18
|
+
const extendedBox = (type, content)=>{
|
|
19
|
+
const header = Buffer.alloc(16);
|
|
20
|
+
header.writeUInt32BE(1);
|
|
21
|
+
header.write(type, 4, 4, 'latin1');
|
|
22
|
+
header.writeBigUInt64BE(BigInt(content.length + 16), 8);
|
|
23
|
+
return Buffer.concat([
|
|
24
|
+
header,
|
|
25
|
+
content
|
|
26
|
+
]);
|
|
27
|
+
};
|
|
28
|
+
const fileTypeContent = (brand = 'avif')=>{
|
|
29
|
+
const content = Buffer.alloc(12);
|
|
30
|
+
content.write(brand, 0, 4, 'latin1');
|
|
31
|
+
content.write(brand, 8, 4, 'latin1');
|
|
32
|
+
return content;
|
|
33
|
+
};
|
|
34
|
+
const validFile = (brand = 'avif')=>Buffer.concat([
|
|
35
|
+
box('ftyp', fileTypeContent(brand)),
|
|
36
|
+
box('free')
|
|
37
|
+
]);
|
|
38
|
+
afterEach(async ()=>{
|
|
39
|
+
await Promise.all(tempFiles.splice(0).map((filePath)=>fs.unlink(filePath)));
|
|
40
|
+
});
|
|
41
|
+
describe('ISO base media file validation', ()=>{
|
|
42
|
+
it.each([
|
|
43
|
+
'audio/mp4',
|
|
44
|
+
'audio/x-m4a',
|
|
45
|
+
'image/avif',
|
|
46
|
+
'image/heic',
|
|
47
|
+
'image/heic-sequence',
|
|
48
|
+
'image/heif',
|
|
49
|
+
'image/heif-sequence',
|
|
50
|
+
'image/x-canon-cr3',
|
|
51
|
+
'video/3gpp',
|
|
52
|
+
'video/3gpp2',
|
|
53
|
+
'video/mp4',
|
|
54
|
+
'video/quicktime',
|
|
55
|
+
'video/x-m4v'
|
|
56
|
+
])('should identify %s as an ISO base media type', (mimeType)=>{
|
|
57
|
+
expect(isISOBaseMediaMimeType(mimeType)).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
it('should preserve unrelated media types', ()=>{
|
|
60
|
+
expect(isISOBaseMediaMimeType('image/png')).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
it('should accept normal and extended file type boxes', async ()=>{
|
|
63
|
+
await expect(validateISOBaseMediaBuffer(validFile())).resolves.toBe(true);
|
|
64
|
+
await expect(validateISOBaseMediaBuffer(Buffer.concat([
|
|
65
|
+
extendedBox('ftyp', fileTypeContent('heic')),
|
|
66
|
+
box('free')
|
|
67
|
+
]))).resolves.toBe(true);
|
|
68
|
+
});
|
|
69
|
+
it('should accept a final box that extends to the end of the file', async ()=>{
|
|
70
|
+
const finalBox = box('mdat', Buffer.from('reference'), 0);
|
|
71
|
+
await expect(validateISOBaseMediaBuffer(Buffer.concat([
|
|
72
|
+
box('ftyp', fileTypeContent()),
|
|
73
|
+
finalBox
|
|
74
|
+
]))).resolves.toBe(true);
|
|
75
|
+
});
|
|
76
|
+
it.each([
|
|
77
|
+
'free',
|
|
78
|
+
'mdat',
|
|
79
|
+
'moov',
|
|
80
|
+
'wide'
|
|
81
|
+
])('should accept a QuickTime file beginning with %s', async (type)=>{
|
|
82
|
+
const content = Buffer.concat([
|
|
83
|
+
box(type),
|
|
84
|
+
box('mdat', Buffer.from('reference'))
|
|
85
|
+
]);
|
|
86
|
+
await expect(fileTypeFromBuffer(content)).resolves.toMatchObject({
|
|
87
|
+
mime: 'video/quicktime'
|
|
88
|
+
});
|
|
89
|
+
await expect(validateISOBaseMediaBuffer(content)).resolves.toBe(true);
|
|
90
|
+
});
|
|
91
|
+
it.each([
|
|
92
|
+
[
|
|
93
|
+
'missing following box',
|
|
94
|
+
box('ftyp', fileTypeContent())
|
|
95
|
+
],
|
|
96
|
+
[
|
|
97
|
+
'file type box extending to end',
|
|
98
|
+
box('ftyp', fileTypeContent(), 0)
|
|
99
|
+
],
|
|
100
|
+
[
|
|
101
|
+
'short file type payload',
|
|
102
|
+
box('ftyp', Buffer.from('avif'), 12)
|
|
103
|
+
],
|
|
104
|
+
[
|
|
105
|
+
'unaligned compatible brands',
|
|
106
|
+
box('ftyp', Buffer.alloc(9), 17)
|
|
107
|
+
],
|
|
108
|
+
[
|
|
109
|
+
'box extending beyond the file',
|
|
110
|
+
box('ftyp', fileTypeContent(), 128)
|
|
111
|
+
],
|
|
112
|
+
[
|
|
113
|
+
'trailing partial header',
|
|
114
|
+
Buffer.concat([
|
|
115
|
+
validFile(),
|
|
116
|
+
Buffer.alloc(4)
|
|
117
|
+
])
|
|
118
|
+
]
|
|
119
|
+
])('should reject %s', async (_, content)=>{
|
|
120
|
+
await expect(validateISOBaseMediaBuffer(content)).resolves.toBe(false);
|
|
121
|
+
});
|
|
122
|
+
it('should validate file-backed uploads without loading the full file', async ()=>{
|
|
123
|
+
const filePath = path.join(os.tmpdir(), `payload-media-${randomUUID()}`);
|
|
124
|
+
tempFiles.push(filePath);
|
|
125
|
+
await fs.writeFile(filePath, validFile('mif1'));
|
|
126
|
+
await expect(validateISOBaseMediaFile(filePath)).resolves.toBe(true);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
//# sourceMappingURL=validateISOBaseMediaFile.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/uploads/validateISOBaseMediaFile.spec.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport { randomUUID } from 'crypto'\nimport { fileTypeFromBuffer } from 'file-type'\nimport os from 'os'\nimport path from 'path'\nimport { afterEach, describe, expect, it } from 'vitest'\n\nimport {\n isISOBaseMediaMimeType,\n validateISOBaseMediaBuffer,\n validateISOBaseMediaFile,\n} from './validateISOBaseMediaFile.js'\n\nconst tempFiles: string[] = []\n\nconst box = (type: string, content = Buffer.alloc(0), size = content.length + 8): Buffer => {\n const header = Buffer.alloc(8)\n header.writeUInt32BE(size)\n header.write(type, 4, 4, 'latin1')\n return Buffer.concat([header, content])\n}\n\nconst extendedBox = (type: string, content: Buffer): Buffer => {\n const header = Buffer.alloc(16)\n header.writeUInt32BE(1)\n header.write(type, 4, 4, 'latin1')\n header.writeBigUInt64BE(BigInt(content.length + 16), 8)\n return Buffer.concat([header, content])\n}\n\nconst fileTypeContent = (brand = 'avif'): Buffer => {\n const content = Buffer.alloc(12)\n content.write(brand, 0, 4, 'latin1')\n content.write(brand, 8, 4, 'latin1')\n return content\n}\n\nconst validFile = (brand = 'avif'): Buffer =>\n Buffer.concat([box('ftyp', fileTypeContent(brand)), box('free')])\n\nafterEach(async () => {\n await Promise.all(tempFiles.splice(0).map((filePath) => fs.unlink(filePath)))\n})\n\ndescribe('ISO base media file validation', () => {\n it.each([\n 'audio/mp4',\n 'audio/x-m4a',\n 'image/avif',\n 'image/heic',\n 'image/heic-sequence',\n 'image/heif',\n 'image/heif-sequence',\n 'image/x-canon-cr3',\n 'video/3gpp',\n 'video/3gpp2',\n 'video/mp4',\n 'video/quicktime',\n 'video/x-m4v',\n ])('should identify %s as an ISO base media type', (mimeType) => {\n expect(isISOBaseMediaMimeType(mimeType)).toBe(true)\n })\n\n it('should preserve unrelated media types', () => {\n expect(isISOBaseMediaMimeType('image/png')).toBe(false)\n })\n\n it('should accept normal and extended file type boxes', async () => {\n await expect(validateISOBaseMediaBuffer(validFile())).resolves.toBe(true)\n await expect(\n validateISOBaseMediaBuffer(\n Buffer.concat([extendedBox('ftyp', fileTypeContent('heic')), box('free')]),\n ),\n ).resolves.toBe(true)\n })\n\n it('should accept a final box that extends to the end of the file', async () => {\n const finalBox = box('mdat', Buffer.from('reference'), 0)\n await expect(\n validateISOBaseMediaBuffer(Buffer.concat([box('ftyp', fileTypeContent()), finalBox])),\n ).resolves.toBe(true)\n })\n\n it.each(['free', 'mdat', 'moov', 'wide'])(\n 'should accept a QuickTime file beginning with %s',\n async (type) => {\n const content = Buffer.concat([box(type), box('mdat', Buffer.from('reference'))])\n await expect(fileTypeFromBuffer(content)).resolves.toMatchObject({ mime: 'video/quicktime' })\n await expect(validateISOBaseMediaBuffer(content)).resolves.toBe(true)\n },\n )\n\n it.each([\n ['missing following box', box('ftyp', fileTypeContent())],\n ['file type box extending to end', box('ftyp', fileTypeContent(), 0)],\n ['short file type payload', box('ftyp', Buffer.from('avif'), 12)],\n ['unaligned compatible brands', box('ftyp', Buffer.alloc(9), 17)],\n ['box extending beyond the file', box('ftyp', fileTypeContent(), 128)],\n ['trailing partial header', Buffer.concat([validFile(), Buffer.alloc(4)])],\n ])('should reject %s', async (_, content) => {\n await expect(validateISOBaseMediaBuffer(content)).resolves.toBe(false)\n })\n\n it('should validate file-backed uploads without loading the full file', async () => {\n const filePath = path.join(os.tmpdir(), `payload-media-${randomUUID()}`)\n tempFiles.push(filePath)\n await fs.writeFile(filePath, validFile('mif1'))\n\n await expect(validateISOBaseMediaFile(filePath)).resolves.toBe(true)\n })\n})\n"],"names":["fs","randomUUID","fileTypeFromBuffer","os","path","afterEach","describe","expect","it","isISOBaseMediaMimeType","validateISOBaseMediaBuffer","validateISOBaseMediaFile","tempFiles","box","type","content","Buffer","alloc","size","length","header","writeUInt32BE","write","concat","extendedBox","writeBigUInt64BE","BigInt","fileTypeContent","brand","validFile","Promise","all","splice","map","filePath","unlink","each","mimeType","toBe","resolves","finalBox","from","toMatchObject","mime","_","join","tmpdir","push","writeFile"],"mappings":"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"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export type SvgInspection = {
|
|
2
|
+
isSafe: boolean;
|
|
3
|
+
isSvg: boolean;
|
|
4
|
+
isValid: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function inspectSvg(buffer: Buffer): SvgInspection;
|
|
7
|
+
export declare function inspectSvgFile(filePath: string, options?: {
|
|
8
|
+
highWaterMark?: number;
|
|
9
|
+
}): Promise<SvgInspection>;
|
|
10
|
+
/** Validate SVG content by parsing its XML structure and rejecting active content. */
|
|
5
11
|
export declare function validateSvg(buffer: Buffer): boolean;
|
|
6
12
|
//# sourceMappingURL=validateSvg.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateSvg.d.ts","sourceRoot":"","sources":["../../src/uploads/validateSvg.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validateSvg.d.ts","sourceRoot":"","sources":["../../src/uploads/validateSvg.ts"],"names":[],"mappings":"AAuEA,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAmMD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAiBxD;AAED,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACnC,OAAO,CAAC,aAAa,CAAC,CAiCxB;AAED,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnD"}
|
|
@@ -1,46 +1,286 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { createReadStream } from 'fs';
|
|
2
|
+
import sax from 'sax';
|
|
3
|
+
import { TextDecoder } from 'util';
|
|
4
|
+
const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
5
|
+
const blockedElements = new Set([
|
|
6
|
+
'embed',
|
|
7
|
+
'foreignobject',
|
|
8
|
+
'iframe',
|
|
9
|
+
'object',
|
|
10
|
+
'script'
|
|
11
|
+
]);
|
|
12
|
+
const animationElements = new Set([
|
|
13
|
+
'animate',
|
|
14
|
+
'animatemotion',
|
|
15
|
+
'animatetransform',
|
|
16
|
+
'set'
|
|
17
|
+
]);
|
|
18
|
+
const animationValueAttributes = new Set([
|
|
19
|
+
'by',
|
|
20
|
+
'from',
|
|
21
|
+
'to',
|
|
22
|
+
'values'
|
|
23
|
+
]);
|
|
24
|
+
const urlAttributes = new Set([
|
|
25
|
+
'data',
|
|
26
|
+
'href',
|
|
27
|
+
'src'
|
|
28
|
+
]);
|
|
29
|
+
const urlFunctionAttributes = new Set([
|
|
30
|
+
'clip-path',
|
|
31
|
+
'cursor',
|
|
32
|
+
'fill',
|
|
33
|
+
'filter',
|
|
34
|
+
'marker',
|
|
35
|
+
'marker-end',
|
|
36
|
+
'marker-mid',
|
|
37
|
+
'marker-start',
|
|
38
|
+
'mask',
|
|
39
|
+
'stroke'
|
|
40
|
+
]);
|
|
41
|
+
const getLocalName = (name)=>{
|
|
42
|
+
const separator = name.lastIndexOf(':');
|
|
43
|
+
return (separator === -1 ? name : name.slice(separator + 1)).toLowerCase();
|
|
44
|
+
};
|
|
45
|
+
const getPrefix = (name)=>{
|
|
46
|
+
const separator = name.indexOf(':');
|
|
47
|
+
return separator === -1 ? '' : name.slice(0, separator);
|
|
48
|
+
};
|
|
49
|
+
const hasBlockedURL = (value)=>{
|
|
50
|
+
let normalizedValue = '';
|
|
51
|
+
for (const character of value){
|
|
52
|
+
const codePoint = character.codePointAt(0);
|
|
53
|
+
if (codePoint && codePoint > 0x20 && codePoint !== 0x7f) {
|
|
54
|
+
normalizedValue += character;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
normalizedValue = normalizedValue.toLowerCase();
|
|
58
|
+
return normalizedValue.includes('javascript:') || normalizedValue.includes('data:text/html') || normalizedValue.includes('data:image/svg+xml');
|
|
59
|
+
};
|
|
60
|
+
const decodeCSSValue = (value)=>value.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\\\r\n|\\[\n\r\f]/g, '').replace(/\\([\da-f]{1,6})\s?|\\(.)/gi, (_match, hexadecimal, escapedCharacter)=>{
|
|
61
|
+
if (hexadecimal) {
|
|
62
|
+
const codePoint = Number.parseInt(hexadecimal, 16);
|
|
63
|
+
return codePoint > 0 && codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : '';
|
|
64
|
+
}
|
|
65
|
+
return escapedCharacter || '';
|
|
66
|
+
});
|
|
67
|
+
const hasBlockedStyles = (value)=>{
|
|
68
|
+
const decodedValue = decodeCSSValue(value);
|
|
69
|
+
return decodedValue.toLowerCase().includes('@import') || hasBlockedURL(decodedValue);
|
|
70
|
+
};
|
|
71
|
+
const getXMLDecoder = (buffer)=>{
|
|
72
|
+
if (buffer[0] === 0xff && buffer[1] === 0xfe || buffer[0] === 0x3c && buffer[1] === 0x00 && buffer[2] === 0x3f && buffer[3] === 0x00) {
|
|
73
|
+
return new TextDecoder('utf-16le');
|
|
74
|
+
}
|
|
75
|
+
if (buffer[0] === 0xfe && buffer[1] === 0xff || buffer[0] === 0x00 && buffer[1] === 0x3c && buffer[2] === 0x00 && buffer[3] === 0x3f) {
|
|
76
|
+
return new TextDecoder('utf-16be');
|
|
77
|
+
}
|
|
78
|
+
return new TextDecoder('utf-8');
|
|
79
|
+
};
|
|
80
|
+
const createSvgInspector = ()=>{
|
|
81
|
+
// sax's namespace mode walks prototype chains at every close tag. Payload only needs to resolve
|
|
82
|
+
// the root SVG namespace; active descendants and attributes are checked by local name.
|
|
83
|
+
const saxWithBufferLength = sax;
|
|
84
|
+
const maxBufferLength = saxWithBufferLength.MAX_BUFFER_LENGTH;
|
|
85
|
+
let parser;
|
|
86
|
+
try {
|
|
87
|
+
saxWithBufferLength.MAX_BUFFER_LENGTH = Number.POSITIVE_INFINITY;
|
|
88
|
+
parser = sax.parser(true, {
|
|
89
|
+
position: false,
|
|
90
|
+
strictEntities: true,
|
|
91
|
+
xmlns: false
|
|
92
|
+
});
|
|
93
|
+
} finally{
|
|
94
|
+
saxWithBufferLength.MAX_BUFFER_LENGTH = maxBufferLength;
|
|
95
|
+
}
|
|
96
|
+
let attributes = [];
|
|
97
|
+
const elementNames = [];
|
|
98
|
+
let isSafe = true;
|
|
99
|
+
let isSvg = false;
|
|
100
|
+
let isValid = true;
|
|
101
|
+
let rootElementSeen = false;
|
|
102
|
+
let styleContent = null;
|
|
103
|
+
let styleElementDepth = 0;
|
|
104
|
+
parser.onerror = (error)=>{
|
|
105
|
+
throw error;
|
|
106
|
+
};
|
|
107
|
+
parser.onprocessinginstruction = ({ name })=>{
|
|
108
|
+
if (name.toLowerCase() === 'xml-stylesheet') {
|
|
109
|
+
isSafe = false;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
parser.ondoctype = (doctype)=>{
|
|
113
|
+
if (doctype.includes('[') || /<!entity/i.test(doctype)) {
|
|
114
|
+
isSafe = false;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
parser.onopentagstart = (tag)=>{
|
|
118
|
+
attributes = [];
|
|
119
|
+
// sax stores attributes in a normal object and calls its hasOwnProperty method. Use a guarded
|
|
120
|
+
// null-prototype object so valid XML attributes with object-property names remain supported.
|
|
121
|
+
const saxAttributes = Object.create(null);
|
|
122
|
+
tag.attributes = new Proxy(saxAttributes, {
|
|
123
|
+
get: (target, property, receiver)=>{
|
|
124
|
+
if (property === 'hasOwnProperty') {
|
|
125
|
+
return (name)=>Object.prototype.hasOwnProperty.call(target, name);
|
|
126
|
+
}
|
|
127
|
+
return Reflect.get(target, property, receiver);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
parser.onattribute = (attribute)=>{
|
|
132
|
+
attributes.push(attribute);
|
|
133
|
+
};
|
|
134
|
+
parser.onopentag = (tag)=>{
|
|
135
|
+
const localName = getLocalName(tag.name);
|
|
136
|
+
elementNames.push(localName);
|
|
137
|
+
if (!rootElementSeen) {
|
|
138
|
+
rootElementSeen = true;
|
|
139
|
+
const prefix = getPrefix(tag.name);
|
|
140
|
+
const namespaceAttribute = prefix ? `xmlns:${prefix}` : 'xmlns';
|
|
141
|
+
const namespace = attributes.find(({ name })=>name === namespaceAttribute)?.value;
|
|
142
|
+
isSvg = localName === 'svg' && namespace === SVG_NAMESPACE;
|
|
143
|
+
if (!isSvg) {
|
|
144
|
+
isSafe = false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (blockedElements.has(localName)) {
|
|
148
|
+
isSafe = false;
|
|
149
|
+
}
|
|
150
|
+
if (localName === 'style') {
|
|
151
|
+
styleElementDepth += 1;
|
|
152
|
+
if (styleElementDepth === 1) {
|
|
153
|
+
styleContent = '';
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (animationElements.has(localName)) {
|
|
157
|
+
const targetAttribute = attributes.find(({ name })=>getLocalName(name).toLowerCase() === 'attributename')?.value.trim();
|
|
158
|
+
const targetName = targetAttribute ? getLocalName(targetAttribute) : '';
|
|
159
|
+
const values = attributes.filter(({ name })=>animationValueAttributes.has(getLocalName(name))).map(({ value })=>value).join(';');
|
|
160
|
+
if (/^on[a-z]/.test(targetName) || urlAttributes.has(targetName) && hasBlockedURL(values) || targetName === 'style' && hasBlockedStyles(values) || urlFunctionAttributes.has(targetName) && hasBlockedStyles(values)) {
|
|
161
|
+
isSafe = false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
for (const attribute of attributes){
|
|
165
|
+
const attributeName = getLocalName(attribute.name);
|
|
166
|
+
if (/^on[a-z]/.test(attributeName)) {
|
|
167
|
+
isSafe = false;
|
|
168
|
+
}
|
|
169
|
+
if (attributeName === 'style' && hasBlockedStyles(attribute.value)) {
|
|
170
|
+
isSafe = false;
|
|
171
|
+
}
|
|
172
|
+
if (urlAttributes.has(attributeName) && hasBlockedURL(attribute.value)) {
|
|
173
|
+
isSafe = false;
|
|
174
|
+
}
|
|
175
|
+
if (urlFunctionAttributes.has(attributeName) && /url\s*\(/i.test(attribute.value) && hasBlockedStyles(attribute.value)) {
|
|
176
|
+
isSafe = false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
parser.onclosetag = ()=>{
|
|
181
|
+
if (elementNames.pop() === 'style') {
|
|
182
|
+
if (styleElementDepth === 1 && styleContent && hasBlockedStyles(styleContent)) {
|
|
183
|
+
isSafe = false;
|
|
184
|
+
}
|
|
185
|
+
styleElementDepth -= 1;
|
|
186
|
+
if (styleElementDepth === 0) {
|
|
187
|
+
styleContent = null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const collectStyleContent = (value)=>{
|
|
192
|
+
if (styleElementDepth > 0 && styleContent !== null) {
|
|
193
|
+
styleContent += value;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
parser.ontext = collectStyleContent;
|
|
197
|
+
parser.oncdata = collectStyleContent;
|
|
198
|
+
return {
|
|
199
|
+
close: ()=>{
|
|
200
|
+
parser.close();
|
|
201
|
+
if (!rootElementSeen) {
|
|
202
|
+
throw new Error('Missing root element');
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
getResult: ()=>({
|
|
206
|
+
isSafe: rootElementSeen && isSafe && isValid,
|
|
207
|
+
isSvg,
|
|
208
|
+
isValid
|
|
209
|
+
}),
|
|
210
|
+
hasNonSvgRoot: ()=>rootElementSeen && !isSvg,
|
|
211
|
+
markInvalid: ()=>{
|
|
212
|
+
isValid = false;
|
|
213
|
+
},
|
|
214
|
+
write: (value)=>parser.write(value)
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
export function inspectSvg(buffer) {
|
|
218
|
+
const inspector = createSvgInspector();
|
|
219
|
+
const decoder = getXMLDecoder(buffer);
|
|
5
220
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/<foreignObject[\s>]/i,
|
|
18
|
-
// Embedded iframes
|
|
19
|
-
/<iframe[\s>]/i,
|
|
20
|
-
// Embedded objects and embeds
|
|
21
|
-
/<object[\s>]/i,
|
|
22
|
-
/<embed[\s>]/i,
|
|
23
|
-
// Base64 encoded scripts (common obfuscation technique)
|
|
24
|
-
/data:image\/svg\+xml;base64,[\w+/]*PHNjcmlwdA/i,
|
|
25
|
-
// XLink href with javascript (deprecated but still dangerous)
|
|
26
|
-
/xlink:href\s*=\s*["']javascript:/i,
|
|
27
|
-
// Import statements
|
|
28
|
-
/@import/i,
|
|
29
|
-
// External resource references that could be dangerous
|
|
30
|
-
/<!ENTITY/i,
|
|
31
|
-
/<!DOCTYPE[^>]*\[/i,
|
|
32
|
-
// Attempt to use CDATA to hide scripts
|
|
33
|
-
/<!\[CDATA\[[\s\S]*<script/i
|
|
34
|
-
];
|
|
35
|
-
for (const pattern of dangerousPatterns){
|
|
36
|
-
if (pattern.test(content)) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return true;
|
|
221
|
+
for(let offset = 0; offset < buffer.length; offset += 64 * 1024){
|
|
222
|
+
inspector.write(decoder.decode(buffer.subarray(offset, offset + 64 * 1024), {
|
|
223
|
+
stream: true
|
|
224
|
+
}));
|
|
225
|
+
if (inspector.hasNonSvgRoot()) {
|
|
226
|
+
return inspector.getResult();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
inspector.write(decoder.decode());
|
|
230
|
+
inspector.close();
|
|
231
|
+
return inspector.getResult();
|
|
41
232
|
} catch (_error) {
|
|
42
|
-
|
|
233
|
+
inspector.markInvalid();
|
|
234
|
+
return {
|
|
235
|
+
...inspector.getResult(),
|
|
236
|
+
isSafe: false
|
|
237
|
+
};
|
|
43
238
|
}
|
|
44
239
|
}
|
|
240
|
+
export async function inspectSvgFile(filePath, options) {
|
|
241
|
+
const inspector = createSvgInspector();
|
|
242
|
+
let decoder = null;
|
|
243
|
+
let prefix = Buffer.alloc(0);
|
|
244
|
+
try {
|
|
245
|
+
for await (const chunk of createReadStream(filePath, options)){
|
|
246
|
+
let content = chunk;
|
|
247
|
+
if (!decoder) {
|
|
248
|
+
prefix = Buffer.concat([
|
|
249
|
+
prefix,
|
|
250
|
+
content
|
|
251
|
+
]);
|
|
252
|
+
if (prefix.length < 4) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
decoder = getXMLDecoder(prefix);
|
|
256
|
+
content = prefix;
|
|
257
|
+
}
|
|
258
|
+
inspector.write(decoder.decode(content, {
|
|
259
|
+
stream: true
|
|
260
|
+
}));
|
|
261
|
+
if (inspector.hasNonSvgRoot()) {
|
|
262
|
+
return inspector.getResult();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
decoder ??= getXMLDecoder(prefix);
|
|
266
|
+
if (prefix.length > 0 && prefix.length < 4) {
|
|
267
|
+
inspector.write(decoder.decode(prefix, {
|
|
268
|
+
stream: true
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
271
|
+
inspector.write(decoder.decode());
|
|
272
|
+
inspector.close();
|
|
273
|
+
return inspector.getResult();
|
|
274
|
+
} catch (_error) {
|
|
275
|
+
inspector.markInvalid();
|
|
276
|
+
return {
|
|
277
|
+
...inspector.getResult(),
|
|
278
|
+
isSafe: false
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/** Validate SVG content by parsing its XML structure and rejecting active content. */ export function validateSvg(buffer) {
|
|
283
|
+
return inspectSvg(buffer).isSafe;
|
|
284
|
+
}
|
|
45
285
|
|
|
46
286
|
//# sourceMappingURL=validateSvg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/uploads/validateSvg.ts"],"sourcesContent":["/**\n * Validate SVG content for security vulnerabilities\n * Detects and blocks malicious patterns commonly used in SVG-based attacks\n */\nexport function validateSvg(buffer: Buffer): boolean {\n try {\n const content = buffer.toString('utf8')\n\n const dangerousPatterns = [\n // Script tags\n /<script[\\s>]/i,\n /<\\/script>/i,\n\n // Event handlers (onclick, onload, onerror, etc.)\n /\\son\\w+\\s*=/i,\n\n // JavaScript URLs\n /javascript:/i,\n /data:text\\/html/i,\n\n // Foreign objects (can embed HTML)\n /<foreignObject[\\s>]/i,\n\n // Embedded iframes\n /<iframe[\\s>]/i,\n\n // Embedded objects and embeds\n /<object[\\s>]/i,\n /<embed[\\s>]/i,\n\n // Base64 encoded scripts (common obfuscation technique)\n /data:image\\/svg\\+xml;base64,[\\w+/]*PHNjcmlwdA/i, // <script in base64\n\n // XLink href with javascript (deprecated but still dangerous)\n /xlink:href\\s*=\\s*[\"']javascript:/i,\n\n // Import statements\n /@import/i,\n\n // External resource references that could be dangerous\n /<!ENTITY/i,\n /<!DOCTYPE[^>]*\\[/i, // DOCTYPE with internal subset\n\n // Attempt to use CDATA to hide scripts\n /<!\\[CDATA\\[[\\s\\S]*<script/i,\n ]\n\n for (const pattern of dangerousPatterns) {\n if (pattern.test(content)) {\n return false\n }\n }\n\n return true\n } catch (_error) {\n return false\n }\n}\n"],"names":["validateSvg","buffer","content","toString","dangerousPatterns","pattern","test","_error"],"mappings":"AAAA;;;CAGC,GACD,OAAO,SAASA,YAAYC,MAAc;IACxC,IAAI;QACF,MAAMC,UAAUD,OAAOE,QAAQ,CAAC;QAEhC,MAAMC,oBAAoB;YACxB,cAAc;YACd;YACA;YAEA,kDAAkD;YAClD;YAEA,kBAAkB;YAClB;YACA;YAEA,mCAAmC;YACnC;YAEA,mBAAmB;YACnB;YAEA,8BAA8B;YAC9B;YACA;YAEA,wDAAwD;YACxD;YAEA,8DAA8D;YAC9D;YAEA,oBAAoB;YACpB;YAEA,uDAAuD;YACvD;YACA;YAEA,uCAAuC;YACvC;SACD;QAED,KAAK,MAAMC,WAAWD,kBAAmB;YACvC,IAAIC,QAAQC,IAAI,CAACJ,UAAU;gBACzB,OAAO;YACT;QACF;QAEA,OAAO;IACT,EAAE,OAAOK,QAAQ;QACf,OAAO;IACT;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../src/uploads/validateSvg.ts"],"sourcesContent":["import { createReadStream } from 'fs'\nimport sax, { type SAXOptions } from 'sax'\nimport { TextDecoder } from 'util'\n\nconst SVG_NAMESPACE = 'http://www.w3.org/2000/svg'\n\nconst blockedElements = new Set(['embed', 'foreignobject', 'iframe', 'object', 'script'])\nconst animationElements = new Set(['animate', 'animatemotion', 'animatetransform', 'set'])\nconst animationValueAttributes = new Set(['by', 'from', 'to', 'values'])\nconst urlAttributes = new Set(['data', 'href', 'src'])\nconst urlFunctionAttributes = new Set([\n 'clip-path',\n 'cursor',\n 'fill',\n 'filter',\n 'marker',\n 'marker-end',\n 'marker-mid',\n 'marker-start',\n 'mask',\n 'stroke',\n])\n\nconst getLocalName = (name: string): string => {\n const separator = name.lastIndexOf(':')\n return (separator === -1 ? name : name.slice(separator + 1)).toLowerCase()\n}\n\nconst getPrefix = (name: string): string => {\n const separator = name.indexOf(':')\n return separator === -1 ? '' : name.slice(0, separator)\n}\n\nconst hasBlockedURL = (value: string): boolean => {\n let normalizedValue = ''\n\n for (const character of value) {\n const codePoint = character.codePointAt(0)\n if (codePoint && codePoint > 0x20 && codePoint !== 0x7f) {\n normalizedValue += character\n }\n }\n\n normalizedValue = normalizedValue.toLowerCase()\n\n return (\n normalizedValue.includes('javascript:') ||\n normalizedValue.includes('data:text/html') ||\n normalizedValue.includes('data:image/svg+xml')\n )\n}\n\nconst decodeCSSValue = (value: string): string =>\n value\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\\\\\r\\n|\\\\[\\n\\r\\f]/g, '')\n .replace(/\\\\([\\da-f]{1,6})\\s?|\\\\(.)/gi, (_match, hexadecimal, escapedCharacter) => {\n if (hexadecimal) {\n const codePoint = Number.parseInt(hexadecimal, 16)\n return codePoint > 0 && codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : ''\n }\n\n return escapedCharacter || ''\n })\n\nconst hasBlockedStyles = (value: string): boolean => {\n const decodedValue = decodeCSSValue(value)\n\n return decodedValue.toLowerCase().includes('@import') || hasBlockedURL(decodedValue)\n}\n\nexport type SvgInspection = {\n isSafe: boolean\n isSvg: boolean\n isValid: boolean\n}\n\nconst getXMLDecoder = (buffer: Buffer): TextDecoder => {\n if (\n (buffer[0] === 0xff && buffer[1] === 0xfe) ||\n (buffer[0] === 0x3c && buffer[1] === 0x00 && buffer[2] === 0x3f && buffer[3] === 0x00)\n ) {\n return new TextDecoder('utf-16le')\n }\n\n if (\n (buffer[0] === 0xfe && buffer[1] === 0xff) ||\n (buffer[0] === 0x00 && buffer[1] === 0x3c && buffer[2] === 0x00 && buffer[3] === 0x3f)\n ) {\n return new TextDecoder('utf-16be')\n }\n\n return new TextDecoder('utf-8')\n}\n\nconst createSvgInspector = () => {\n // sax's namespace mode walks prototype chains at every close tag. Payload only needs to resolve\n // the root SVG namespace; active descendants and attributes are checked by local name.\n const saxWithBufferLength = sax as { MAX_BUFFER_LENGTH: number } & typeof sax\n const maxBufferLength = saxWithBufferLength.MAX_BUFFER_LENGTH\n let parser: ReturnType<typeof sax.parser>\n try {\n saxWithBufferLength.MAX_BUFFER_LENGTH = Number.POSITIVE_INFINITY\n parser = sax.parser(true, {\n position: false,\n strictEntities: true,\n xmlns: false,\n } as { strictEntities: boolean } & SAXOptions)\n } finally {\n saxWithBufferLength.MAX_BUFFER_LENGTH = maxBufferLength\n }\n\n let attributes: Array<{ name: string; value: string }> = []\n const elementNames: string[] = []\n let isSafe = true\n let isSvg = false\n let isValid = true\n let rootElementSeen = false\n let styleContent: null | string = null\n let styleElementDepth = 0\n\n parser.onerror = (error) => {\n throw error\n }\n\n parser.onprocessinginstruction = ({ name }) => {\n if (name.toLowerCase() === 'xml-stylesheet') {\n isSafe = false\n }\n }\n\n parser.ondoctype = (doctype) => {\n if (doctype.includes('[') || /<!entity/i.test(doctype)) {\n isSafe = false\n }\n }\n\n parser.onopentagstart = (tag) => {\n attributes = []\n\n // sax stores attributes in a normal object and calls its hasOwnProperty method. Use a guarded\n // null-prototype object so valid XML attributes with object-property names remain supported.\n const saxAttributes = Object.create(null) as typeof tag.attributes\n tag.attributes = new Proxy(saxAttributes, {\n get: (target, property, receiver) => {\n if (property === 'hasOwnProperty') {\n return (name: string) => Object.prototype.hasOwnProperty.call(target, name)\n }\n\n return Reflect.get(target, property, receiver)\n },\n })\n }\n\n parser.onattribute = (attribute) => {\n attributes.push(attribute)\n }\n\n parser.onopentag = (tag) => {\n const localName = getLocalName(tag.name)\n elementNames.push(localName)\n\n if (!rootElementSeen) {\n rootElementSeen = true\n const prefix = getPrefix(tag.name)\n const namespaceAttribute = prefix ? `xmlns:${prefix}` : 'xmlns'\n const namespace = attributes.find(({ name }) => name === namespaceAttribute)?.value\n isSvg = localName === 'svg' && namespace === SVG_NAMESPACE\n if (!isSvg) {\n isSafe = false\n }\n }\n\n if (blockedElements.has(localName)) {\n isSafe = false\n }\n\n if (localName === 'style') {\n styleElementDepth += 1\n if (styleElementDepth === 1) {\n styleContent = ''\n }\n }\n\n if (animationElements.has(localName)) {\n const targetAttribute = attributes\n .find(({ name }) => getLocalName(name).toLowerCase() === 'attributename')\n ?.value.trim()\n const targetName = targetAttribute ? getLocalName(targetAttribute) : ''\n const values = attributes\n .filter(({ name }) => animationValueAttributes.has(getLocalName(name)))\n .map(({ value }) => value)\n .join(';')\n\n if (\n /^on[a-z]/.test(targetName) ||\n (urlAttributes.has(targetName) && hasBlockedURL(values)) ||\n (targetName === 'style' && hasBlockedStyles(values)) ||\n (urlFunctionAttributes.has(targetName) && hasBlockedStyles(values))\n ) {\n isSafe = false\n }\n }\n\n for (const attribute of attributes) {\n const attributeName = getLocalName(attribute.name)\n\n if (/^on[a-z]/.test(attributeName)) {\n isSafe = false\n }\n\n if (attributeName === 'style' && hasBlockedStyles(attribute.value)) {\n isSafe = false\n }\n\n if (urlAttributes.has(attributeName) && hasBlockedURL(attribute.value)) {\n isSafe = false\n }\n\n if (\n urlFunctionAttributes.has(attributeName) &&\n /url\\s*\\(/i.test(attribute.value) &&\n hasBlockedStyles(attribute.value)\n ) {\n isSafe = false\n }\n }\n }\n\n parser.onclosetag = () => {\n if (elementNames.pop() === 'style') {\n if (styleElementDepth === 1 && styleContent && hasBlockedStyles(styleContent)) {\n isSafe = false\n }\n styleElementDepth -= 1\n if (styleElementDepth === 0) {\n styleContent = null\n }\n }\n }\n\n const collectStyleContent = (value: string) => {\n if (styleElementDepth > 0 && styleContent !== null) {\n styleContent += value\n }\n }\n\n parser.ontext = collectStyleContent\n parser.oncdata = collectStyleContent\n\n return {\n close: () => {\n parser.close()\n if (!rootElementSeen) {\n throw new Error('Missing root element')\n }\n },\n getResult: (): SvgInspection => ({\n isSafe: rootElementSeen && isSafe && isValid,\n isSvg,\n isValid,\n }),\n hasNonSvgRoot: (): boolean => rootElementSeen && !isSvg,\n markInvalid: () => {\n isValid = false\n },\n write: (value: string) => parser.write(value),\n }\n}\n\nexport function inspectSvg(buffer: Buffer): SvgInspection {\n const inspector = createSvgInspector()\n const decoder = getXMLDecoder(buffer)\n try {\n for (let offset = 0; offset < buffer.length; offset += 64 * 1024) {\n inspector.write(decoder.decode(buffer.subarray(offset, offset + 64 * 1024), { stream: true }))\n if (inspector.hasNonSvgRoot()) {\n return inspector.getResult()\n }\n }\n inspector.write(decoder.decode())\n inspector.close()\n return inspector.getResult()\n } catch (_error) {\n inspector.markInvalid()\n return { ...inspector.getResult(), isSafe: false }\n }\n}\n\nexport async function inspectSvgFile(\n filePath: string,\n options?: { highWaterMark?: number },\n): Promise<SvgInspection> {\n const inspector = createSvgInspector()\n let decoder: null | TextDecoder = null\n let prefix = Buffer.alloc(0)\n\n try {\n for await (const chunk of createReadStream(filePath, options)) {\n let content: Buffer = chunk\n if (!decoder) {\n prefix = Buffer.concat([prefix, content])\n if (prefix.length < 4) {\n continue\n }\n decoder = getXMLDecoder(prefix)\n content = prefix\n }\n\n inspector.write(decoder.decode(content, { stream: true }))\n if (inspector.hasNonSvgRoot()) {\n return inspector.getResult()\n }\n }\n decoder ??= getXMLDecoder(prefix)\n if (prefix.length > 0 && prefix.length < 4) {\n inspector.write(decoder.decode(prefix, { stream: true }))\n }\n inspector.write(decoder.decode())\n inspector.close()\n return inspector.getResult()\n } catch (_error) {\n inspector.markInvalid()\n return { ...inspector.getResult(), isSafe: false }\n }\n}\n\n/** Validate SVG content by parsing its XML structure and rejecting active content. */\nexport function validateSvg(buffer: Buffer): boolean {\n return inspectSvg(buffer).isSafe\n}\n"],"names":["createReadStream","sax","TextDecoder","SVG_NAMESPACE","blockedElements","Set","animationElements","animationValueAttributes","urlAttributes","urlFunctionAttributes","getLocalName","name","separator","lastIndexOf","slice","toLowerCase","getPrefix","indexOf","hasBlockedURL","value","normalizedValue","character","codePoint","codePointAt","includes","decodeCSSValue","replace","_match","hexadecimal","escapedCharacter","Number","parseInt","String","fromCodePoint","hasBlockedStyles","decodedValue","getXMLDecoder","buffer","createSvgInspector","saxWithBufferLength","maxBufferLength","MAX_BUFFER_LENGTH","parser","POSITIVE_INFINITY","position","strictEntities","xmlns","attributes","elementNames","isSafe","isSvg","isValid","rootElementSeen","styleContent","styleElementDepth","onerror","error","onprocessinginstruction","ondoctype","doctype","test","onopentagstart","tag","saxAttributes","Object","create","Proxy","get","target","property","receiver","prototype","hasOwnProperty","call","Reflect","onattribute","attribute","push","onopentag","localName","prefix","namespaceAttribute","namespace","find","has","targetAttribute","trim","targetName","values","filter","map","join","attributeName","onclosetag","pop","collectStyleContent","ontext","oncdata","close","Error","getResult","hasNonSvgRoot","markInvalid","write","inspectSvg","inspector","decoder","offset","length","decode","subarray","stream","_error","inspectSvgFile","filePath","options","Buffer","alloc","chunk","content","concat","validateSvg"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,KAAI;AACrC,OAAOC,SAA8B,MAAK;AAC1C,SAASC,WAAW,QAAQ,OAAM;AAElC,MAAMC,gBAAgB;AAEtB,MAAMC,kBAAkB,IAAIC,IAAI;IAAC;IAAS;IAAiB;IAAU;IAAU;CAAS;AACxF,MAAMC,oBAAoB,IAAID,IAAI;IAAC;IAAW;IAAiB;IAAoB;CAAM;AACzF,MAAME,2BAA2B,IAAIF,IAAI;IAAC;IAAM;IAAQ;IAAM;CAAS;AACvE,MAAMG,gBAAgB,IAAIH,IAAI;IAAC;IAAQ;IAAQ;CAAM;AACrD,MAAMI,wBAAwB,IAAIJ,IAAI;IACpC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,MAAMK,eAAe,CAACC;IACpB,MAAMC,YAAYD,KAAKE,WAAW,CAAC;IACnC,OAAO,AAACD,CAAAA,cAAc,CAAC,IAAID,OAAOA,KAAKG,KAAK,CAACF,YAAY,EAAC,EAAGG,WAAW;AAC1E;AAEA,MAAMC,YAAY,CAACL;IACjB,MAAMC,YAAYD,KAAKM,OAAO,CAAC;IAC/B,OAAOL,cAAc,CAAC,IAAI,KAAKD,KAAKG,KAAK,CAAC,GAAGF;AAC/C;AAEA,MAAMM,gBAAgB,CAACC;IACrB,IAAIC,kBAAkB;IAEtB,KAAK,MAAMC,aAAaF,MAAO;QAC7B,MAAMG,YAAYD,UAAUE,WAAW,CAAC;QACxC,IAAID,aAAaA,YAAY,QAAQA,cAAc,MAAM;YACvDF,mBAAmBC;QACrB;IACF;IAEAD,kBAAkBA,gBAAgBL,WAAW;IAE7C,OACEK,gBAAgBI,QAAQ,CAAC,kBACzBJ,gBAAgBI,QAAQ,CAAC,qBACzBJ,gBAAgBI,QAAQ,CAAC;AAE7B;AAEA,MAAMC,iBAAiB,CAACN,QACtBA,MACGO,OAAO,CAAC,qBAAqB,IAC7BA,OAAO,CAAC,sBAAsB,IAC9BA,OAAO,CAAC,+BAA+B,CAACC,QAAQC,aAAaC;QAC5D,IAAID,aAAa;YACf,MAAMN,YAAYQ,OAAOC,QAAQ,CAACH,aAAa;YAC/C,OAAON,YAAY,KAAKA,aAAa,WAAWU,OAAOC,aAAa,CAACX,aAAa;QACpF;QAEA,OAAOO,oBAAoB;IAC7B;AAEJ,MAAMK,mBAAmB,CAACf;IACxB,MAAMgB,eAAeV,eAAeN;IAEpC,OAAOgB,aAAapB,WAAW,GAAGS,QAAQ,CAAC,cAAcN,cAAciB;AACzE;AAQA,MAAMC,gBAAgB,CAACC;IACrB,IACE,AAACA,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK,QACpCA,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK,MACjF;QACA,OAAO,IAAInC,YAAY;IACzB;IAEA,IACE,AAACmC,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK,QACpCA,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK,QAAQA,MAAM,CAAC,EAAE,KAAK,MACjF;QACA,OAAO,IAAInC,YAAY;IACzB;IAEA,OAAO,IAAIA,YAAY;AACzB;AAEA,MAAMoC,qBAAqB;IACzB,gGAAgG;IAChG,uFAAuF;IACvF,MAAMC,sBAAsBtC;IAC5B,MAAMuC,kBAAkBD,oBAAoBE,iBAAiB;IAC7D,IAAIC;IACJ,IAAI;QACFH,oBAAoBE,iBAAiB,GAAGX,OAAOa,iBAAiB;QAChED,SAASzC,IAAIyC,MAAM,CAAC,MAAM;YACxBE,UAAU;YACVC,gBAAgB;YAChBC,OAAO;QACT;IACF,SAAU;QACRP,oBAAoBE,iBAAiB,GAAGD;IAC1C;IAEA,IAAIO,aAAqD,EAAE;IAC3D,MAAMC,eAAyB,EAAE;IACjC,IAAIC,SAAS;IACb,IAAIC,QAAQ;IACZ,IAAIC,UAAU;IACd,IAAIC,kBAAkB;IACtB,IAAIC,eAA8B;IAClC,IAAIC,oBAAoB;IAExBZ,OAAOa,OAAO,GAAG,CAACC;QAChB,MAAMA;IACR;IAEAd,OAAOe,uBAAuB,GAAG,CAAC,EAAE9C,IAAI,EAAE;QACxC,IAAIA,KAAKI,WAAW,OAAO,kBAAkB;YAC3CkC,SAAS;QACX;IACF;IAEAP,OAAOgB,SAAS,GAAG,CAACC;QAClB,IAAIA,QAAQnC,QAAQ,CAAC,QAAQ,YAAYoC,IAAI,CAACD,UAAU;YACtDV,SAAS;QACX;IACF;IAEAP,OAAOmB,cAAc,GAAG,CAACC;QACvBf,aAAa,EAAE;QAEf,8FAA8F;QAC9F,6FAA6F;QAC7F,MAAMgB,gBAAgBC,OAAOC,MAAM,CAAC;QACpCH,IAAIf,UAAU,GAAG,IAAImB,MAAMH,eAAe;YACxCI,KAAK,CAACC,QAAQC,UAAUC;gBACtB,IAAID,aAAa,kBAAkB;oBACjC,OAAO,CAAC1D,OAAiBqD,OAAOO,SAAS,CAACC,cAAc,CAACC,IAAI,CAACL,QAAQzD;gBACxE;gBAEA,OAAO+D,QAAQP,GAAG,CAACC,QAAQC,UAAUC;YACvC;QACF;IACF;IAEA5B,OAAOiC,WAAW,GAAG,CAACC;QACpB7B,WAAW8B,IAAI,CAACD;IAClB;IAEAlC,OAAOoC,SAAS,GAAG,CAAChB;QAClB,MAAMiB,YAAYrE,aAAaoD,IAAInD,IAAI;QACvCqC,aAAa6B,IAAI,CAACE;QAElB,IAAI,CAAC3B,iBAAiB;YACpBA,kBAAkB;YAClB,MAAM4B,SAAShE,UAAU8C,IAAInD,IAAI;YACjC,MAAMsE,qBAAqBD,SAAS,CAAC,MAAM,EAAEA,QAAQ,GAAG;YACxD,MAAME,YAAYnC,WAAWoC,IAAI,CAAC,CAAC,EAAExE,IAAI,EAAE,GAAKA,SAASsE,qBAAqB9D;YAC9E+B,QAAQ6B,cAAc,SAASG,cAAc/E;YAC7C,IAAI,CAAC+C,OAAO;gBACVD,SAAS;YACX;QACF;QAEA,IAAI7C,gBAAgBgF,GAAG,CAACL,YAAY;YAClC9B,SAAS;QACX;QAEA,IAAI8B,cAAc,SAAS;YACzBzB,qBAAqB;YACrB,IAAIA,sBAAsB,GAAG;gBAC3BD,eAAe;YACjB;QACF;QAEA,IAAI/C,kBAAkB8E,GAAG,CAACL,YAAY;YACpC,MAAMM,kBAAkBtC,WACrBoC,IAAI,CAAC,CAAC,EAAExE,IAAI,EAAE,GAAKD,aAAaC,MAAMI,WAAW,OAAO,kBACvDI,MAAMmE;YACV,MAAMC,aAAaF,kBAAkB3E,aAAa2E,mBAAmB;YACrE,MAAMG,SAASzC,WACZ0C,MAAM,CAAC,CAAC,EAAE9E,IAAI,EAAE,GAAKJ,yBAAyB6E,GAAG,CAAC1E,aAAaC,QAC/D+E,GAAG,CAAC,CAAC,EAAEvE,KAAK,EAAE,GAAKA,OACnBwE,IAAI,CAAC;YAER,IACE,WAAW/B,IAAI,CAAC2B,eACf/E,cAAc4E,GAAG,CAACG,eAAerE,cAAcsE,WAC/CD,eAAe,WAAWrD,iBAAiBsD,WAC3C/E,sBAAsB2E,GAAG,CAACG,eAAerD,iBAAiBsD,SAC3D;gBACAvC,SAAS;YACX;QACF;QAEA,KAAK,MAAM2B,aAAa7B,WAAY;YAClC,MAAM6C,gBAAgBlF,aAAakE,UAAUjE,IAAI;YAEjD,IAAI,WAAWiD,IAAI,CAACgC,gBAAgB;gBAClC3C,SAAS;YACX;YAEA,IAAI2C,kBAAkB,WAAW1D,iBAAiB0C,UAAUzD,KAAK,GAAG;gBAClE8B,SAAS;YACX;YAEA,IAAIzC,cAAc4E,GAAG,CAACQ,kBAAkB1E,cAAc0D,UAAUzD,KAAK,GAAG;gBACtE8B,SAAS;YACX;YAEA,IACExC,sBAAsB2E,GAAG,CAACQ,kBAC1B,YAAYhC,IAAI,CAACgB,UAAUzD,KAAK,KAChCe,iBAAiB0C,UAAUzD,KAAK,GAChC;gBACA8B,SAAS;YACX;QACF;IACF;IAEAP,OAAOmD,UAAU,GAAG;QAClB,IAAI7C,aAAa8C,GAAG,OAAO,SAAS;YAClC,IAAIxC,sBAAsB,KAAKD,gBAAgBnB,iBAAiBmB,eAAe;gBAC7EJ,SAAS;YACX;YACAK,qBAAqB;YACrB,IAAIA,sBAAsB,GAAG;gBAC3BD,eAAe;YACjB;QACF;IACF;IAEA,MAAM0C,sBAAsB,CAAC5E;QAC3B,IAAImC,oBAAoB,KAAKD,iBAAiB,MAAM;YAClDA,gBAAgBlC;QAClB;IACF;IAEAuB,OAAOsD,MAAM,GAAGD;IAChBrD,OAAOuD,OAAO,GAAGF;IAEjB,OAAO;QACLG,OAAO;YACLxD,OAAOwD,KAAK;YACZ,IAAI,CAAC9C,iBAAiB;gBACpB,MAAM,IAAI+C,MAAM;YAClB;QACF;QACAC,WAAW,IAAsB,CAAA;gBAC/BnD,QAAQG,mBAAmBH,UAAUE;gBACrCD;gBACAC;YACF,CAAA;QACAkD,eAAe,IAAejD,mBAAmB,CAACF;QAClDoD,aAAa;YACXnD,UAAU;QACZ;QACAoD,OAAO,CAACpF,QAAkBuB,OAAO6D,KAAK,CAACpF;IACzC;AACF;AAEA,OAAO,SAASqF,WAAWnE,MAAc;IACvC,MAAMoE,YAAYnE;IAClB,MAAMoE,UAAUtE,cAAcC;IAC9B,IAAI;QACF,IAAK,IAAIsE,SAAS,GAAGA,SAAStE,OAAOuE,MAAM,EAAED,UAAU,KAAK,KAAM;YAChEF,UAAUF,KAAK,CAACG,QAAQG,MAAM,CAACxE,OAAOyE,QAAQ,CAACH,QAAQA,SAAS,KAAK,OAAO;gBAAEI,QAAQ;YAAK;YAC3F,IAAIN,UAAUJ,aAAa,IAAI;gBAC7B,OAAOI,UAAUL,SAAS;YAC5B;QACF;QACAK,UAAUF,KAAK,CAACG,QAAQG,MAAM;QAC9BJ,UAAUP,KAAK;QACf,OAAOO,UAAUL,SAAS;IAC5B,EAAE,OAAOY,QAAQ;QACfP,UAAUH,WAAW;QACrB,OAAO;YAAE,GAAGG,UAAUL,SAAS,EAAE;YAAEnD,QAAQ;QAAM;IACnD;AACF;AAEA,OAAO,eAAegE,eACpBC,QAAgB,EAChBC,OAAoC;IAEpC,MAAMV,YAAYnE;IAClB,IAAIoE,UAA8B;IAClC,IAAI1B,SAASoC,OAAOC,KAAK,CAAC;IAE1B,IAAI;QACF,WAAW,MAAMC,SAAStH,iBAAiBkH,UAAUC,SAAU;YAC7D,IAAII,UAAkBD;YACtB,IAAI,CAACZ,SAAS;gBACZ1B,SAASoC,OAAOI,MAAM,CAAC;oBAACxC;oBAAQuC;iBAAQ;gBACxC,IAAIvC,OAAO4B,MAAM,GAAG,GAAG;oBACrB;gBACF;gBACAF,UAAUtE,cAAc4C;gBACxBuC,UAAUvC;YACZ;YAEAyB,UAAUF,KAAK,CAACG,QAAQG,MAAM,CAACU,SAAS;gBAAER,QAAQ;YAAK;YACvD,IAAIN,UAAUJ,aAAa,IAAI;gBAC7B,OAAOI,UAAUL,SAAS;YAC5B;QACF;QACAM,YAAYtE,cAAc4C;QAC1B,IAAIA,OAAO4B,MAAM,GAAG,KAAK5B,OAAO4B,MAAM,GAAG,GAAG;YAC1CH,UAAUF,KAAK,CAACG,QAAQG,MAAM,CAAC7B,QAAQ;gBAAE+B,QAAQ;YAAK;QACxD;QACAN,UAAUF,KAAK,CAACG,QAAQG,MAAM;QAC9BJ,UAAUP,KAAK;QACf,OAAOO,UAAUL,SAAS;IAC5B,EAAE,OAAOY,QAAQ;QACfP,UAAUH,WAAW;QACrB,OAAO;YAAE,GAAGG,UAAUL,SAAS,EAAE;YAAEnD,QAAQ;QAAM;IACnD;AACF;AAEA,oFAAoF,GACpF,OAAO,SAASwE,YAAYpF,MAAc;IACxC,OAAOmE,WAAWnE,QAAQY,MAAM;AAClC"}
|