Merge pull request 'Fix Decap image filename validation' (#2) from review/decap-image-validation into main
Reviewed-on: SloanOps/3031023-xyz#2
This commit is contained in:
@@ -156,6 +156,12 @@ not accept executables, archives, HTML, scripts, or SVG uploads. Asset rights,
|
|||||||
alt text, crop, dimensions, and optimization remain operator review items even
|
alt text, crop, dimensions, and optimization remain operator review items even
|
||||||
when the CMS accepts an upload.
|
when the CMS accepts an upload.
|
||||||
|
|
||||||
|
Decap's image widget applies its field `pattern` to the selected file's base
|
||||||
|
name, not to the repository-relative media path. Keep that UI pattern limited
|
||||||
|
to the approved raster filename extensions; enforce the full
|
||||||
|
`public/images/uploads` path separately in the schema, verifier, and Gitea
|
||||||
|
branch rules below.
|
||||||
|
|
||||||
The media gallery lists only `public/images/uploads`, so the pilot seeds that
|
The media gallery lists only `public/images/uploads`, so the pilot seeds that
|
||||||
directory with the images referenced by CMS-controlled fields. Removing an
|
directory with the images referenced by CMS-controlled fields. Removing an
|
||||||
image from content clears the field and uses the page's text-only or initials
|
image from content clears the field and uses the page's text-only or initials
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ collections:
|
|||||||
required: false
|
required: false
|
||||||
choose_url: false
|
choose_url: false
|
||||||
allow_multiple: false
|
allow_multiple: false
|
||||||
pattern: ['^/images/uploads/[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
pattern: ['^[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
||||||
media_library:
|
media_library:
|
||||||
config:
|
config:
|
||||||
max_file_size: 512000
|
max_file_size: 512000
|
||||||
@@ -181,7 +181,7 @@ collections:
|
|||||||
required: false
|
required: false
|
||||||
choose_url: false
|
choose_url: false
|
||||||
allow_multiple: false
|
allow_multiple: false
|
||||||
pattern: ['^/images/uploads/[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
pattern: ['^[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
||||||
media_library:
|
media_library:
|
||||||
config: { max_file_size: 512000, multiple: false }
|
config: { max_file_size: 512000, multiple: false }
|
||||||
- label: Home hero image description
|
- label: Home hero image description
|
||||||
@@ -194,7 +194,7 @@ collections:
|
|||||||
required: false
|
required: false
|
||||||
choose_url: false
|
choose_url: false
|
||||||
allow_multiple: false
|
allow_multiple: false
|
||||||
pattern: ['^/images/uploads/[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
pattern: ['^[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
||||||
media_library:
|
media_library:
|
||||||
config: { max_file_size: 512000, multiple: false }
|
config: { max_file_size: 512000, multiple: false }
|
||||||
- label: Home story image description
|
- label: Home story image description
|
||||||
@@ -207,7 +207,7 @@ collections:
|
|||||||
required: false
|
required: false
|
||||||
choose_url: false
|
choose_url: false
|
||||||
allow_multiple: false
|
allow_multiple: false
|
||||||
pattern: ['^/images/uploads/[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
pattern: ['^[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
||||||
media_library:
|
media_library:
|
||||||
config: { max_file_size: 512000, multiple: false }
|
config: { max_file_size: 512000, multiple: false }
|
||||||
- label: About hero image description
|
- label: About hero image description
|
||||||
@@ -220,7 +220,7 @@ collections:
|
|||||||
required: false
|
required: false
|
||||||
choose_url: false
|
choose_url: false
|
||||||
allow_multiple: false
|
allow_multiple: false
|
||||||
pattern: ['^/images/uploads/[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
pattern: ['^[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
||||||
media_library:
|
media_library:
|
||||||
config: { max_file_size: 512000, multiple: false }
|
config: { max_file_size: 512000, multiple: false }
|
||||||
- label: About story image description
|
- label: About story image description
|
||||||
@@ -233,7 +233,7 @@ collections:
|
|||||||
required: false
|
required: false
|
||||||
choose_url: false
|
choose_url: false
|
||||||
allow_multiple: false
|
allow_multiple: false
|
||||||
pattern: ['^/images/uploads/[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
pattern: ['^[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
||||||
media_library:
|
media_library:
|
||||||
config: { max_file_size: 512000, multiple: false }
|
config: { max_file_size: 512000, multiple: false }
|
||||||
- label: Services hero image description
|
- label: Services hero image description
|
||||||
@@ -246,7 +246,7 @@ collections:
|
|||||||
required: false
|
required: false
|
||||||
choose_url: false
|
choose_url: false
|
||||||
allow_multiple: false
|
allow_multiple: false
|
||||||
pattern: ['^/images/uploads/[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
pattern: ['^[A-Za-z0-9][A-Za-z0-9._-]*\.(?:jpe?g|png|webp|avif)$', 'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.']
|
||||||
media_library:
|
media_library:
|
||||||
config: { max_file_size: 512000, multiple: false }
|
config: { max_file_size: 512000, multiple: false }
|
||||||
- label: Contact hero image description
|
- label: Contact hero image description
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const markdownField = {
|
|||||||
editor_components: [],
|
editor_components: [],
|
||||||
};
|
};
|
||||||
const cmsImagePattern = [
|
const cmsImagePattern = [
|
||||||
'^/images/uploads/[A-Za-z0-9][A-Za-z0-9._-]*\\.(?:jpe?g|png|webp|avif)$',
|
'^[A-Za-z0-9][A-Za-z0-9._-]*\\.(?:jpe?g|png|webp|avif)$',
|
||||||
'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.',
|
'Choose an approved JPG, PNG, WebP, or AVIF upload. SVG and general files are not allowed.',
|
||||||
];
|
];
|
||||||
const optionalImageField = {
|
const optionalImageField = {
|
||||||
@@ -160,6 +160,8 @@ allFields.forEach(visitField);
|
|||||||
const imageFields = allFields.filter(({ widget }) => widget === 'image');
|
const imageFields = allFields.filter(({ widget }) => widget === 'image');
|
||||||
assert(imageFields.length === 7, 'Only the team image and six fixed page-image fields may expose media selection.');
|
assert(imageFields.length === 7, 'Only the team image and six fixed page-image fields may expose media selection.');
|
||||||
for (const field of imageFields) expectSubset(field, optionalImageField, `image field ${field.name}`);
|
for (const field of imageFields) expectSubset(field, optionalImageField, `image field ${field.name}`);
|
||||||
|
assert(new RegExp(cmsImagePattern[0]).test('example-photo.jpg'), 'Image widget validation must accept the filename Decap passes to field patterns.');
|
||||||
|
assert(!new RegExp(cmsImagePattern[0]).test('example-photo.svg'), 'Image widget validation must reject SVG filenames.');
|
||||||
|
|
||||||
const schema = await readFile('src/content.config.ts', 'utf8');
|
const schema = await readFile('src/content.config.ts', 'utf8');
|
||||||
for (const fragment of [
|
for (const fragment of [
|
||||||
|
|||||||
Reference in New Issue
Block a user