payload 3.80.0-internal.cee0ccf → 3.80.0
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/auth/endpoints/forgotPassword.d.ts.map +1 -1
- package/dist/auth/endpoints/forgotPassword.js +0 -2
- package/dist/auth/endpoints/forgotPassword.js.map +1 -1
- package/dist/auth/extractJWT.d.ts.map +1 -1
- package/dist/auth/extractJWT.js +18 -4
- package/dist/auth/extractJWT.js.map +1 -1
- package/dist/auth/operations/forgotPassword.d.ts.map +1 -1
- package/dist/auth/operations/forgotPassword.js +5 -4
- package/dist/auth/operations/forgotPassword.js.map +1 -1
- package/dist/auth/operations/me.js +5 -5
- package/dist/auth/operations/me.js.map +1 -1
- package/dist/auth/sendVerificationEmail.d.ts.map +1 -1
- package/dist/auth/sendVerificationEmail.js +5 -4
- package/dist/auth/sendVerificationEmail.js.map +1 -1
- package/dist/collections/operations/update.js +1 -1
- package/dist/collections/operations/update.js.map +1 -1
- package/dist/collections/operations/utilities/update.d.ts.map +1 -1
- package/dist/collections/operations/utilities/update.js +2 -1
- package/dist/collections/operations/utilities/update.js.map +1 -1
- package/dist/config/orderable/index.d.ts +2 -2
- package/dist/config/orderable/index.d.ts.map +1 -1
- package/dist/config/orderable/index.js +60 -34
- package/dist/config/orderable/index.js.map +1 -1
- package/dist/config/orderable/utils/buildJoinScopeWhere.d.ts +10 -0
- package/dist/config/orderable/utils/buildJoinScopeWhere.d.ts.map +1 -0
- package/dist/config/orderable/utils/buildJoinScopeWhere.js +43 -0
- package/dist/config/orderable/utils/buildJoinScopeWhere.js.map +1 -0
- package/dist/config/orderable/utils/getJoinScopeContext.d.ts +16 -0
- package/dist/config/orderable/utils/getJoinScopeContext.d.ts.map +1 -0
- package/dist/config/orderable/utils/getJoinScopeContext.js +42 -0
- package/dist/config/orderable/utils/getJoinScopeContext.js.map +1 -0
- package/dist/config/orderable/utils/getJoinScopeWhereFromDocData.d.ts +12 -0
- package/dist/config/orderable/utils/getJoinScopeWhereFromDocData.d.ts.map +1 -0
- package/dist/config/orderable/utils/getJoinScopeWhereFromDocData.js +18 -0
- package/dist/config/orderable/utils/getJoinScopeWhereFromDocData.js.map +1 -0
- package/dist/config/orderable/utils/getValueAtPath.d.ts +5 -0
- package/dist/config/orderable/utils/getValueAtPath.d.ts.map +1 -0
- package/dist/config/orderable/utils/getValueAtPath.js +18 -0
- package/dist/config/orderable/utils/getValueAtPath.js.map +1 -0
- package/dist/config/orderable/utils/resolvePendingTargetKey.d.ts +13 -0
- package/dist/config/orderable/utils/resolvePendingTargetKey.d.ts.map +1 -0
- package/dist/config/orderable/utils/resolvePendingTargetKey.js +24 -0
- package/dist/config/orderable/utils/resolvePendingTargetKey.js.map +1 -0
- package/dist/config/types.d.ts +1 -1
- package/dist/config/types.js.map +1 -1
- package/dist/database/getLocalizedPaths.d.ts.map +1 -1
- package/dist/database/getLocalizedPaths.js +2 -1
- package/dist/database/getLocalizedPaths.js.map +1 -1
- package/dist/database/queryValidation/validateQueryPaths.js +1 -1
- package/dist/database/queryValidation/validateQueryPaths.js.map +1 -1
- package/dist/database/queryValidation/validateSearchParams.d.ts.map +1 -1
- package/dist/database/queryValidation/validateSearchParams.js +2 -1
- package/dist/database/queryValidation/validateSearchParams.js.map +1 -1
- package/dist/database/sanitizeJoinQuery.d.ts.map +1 -1
- package/dist/database/sanitizeJoinQuery.js +6 -0
- package/dist/database/sanitizeJoinQuery.js.map +1 -1
- package/dist/database/types.d.ts +0 -1
- package/dist/database/types.d.ts.map +1 -1
- package/dist/database/types.js.map +1 -1
- package/dist/exports/shared.d.ts +1 -0
- package/dist/exports/shared.d.ts.map +1 -1
- package/dist/exports/shared.js +1 -0
- package/dist/exports/shared.js.map +1 -1
- package/dist/fields/baseFields/slug/index.d.ts +7 -0
- package/dist/fields/baseFields/slug/index.d.ts.map +1 -1
- package/dist/fields/baseFields/slug/index.js +2 -2
- package/dist/fields/baseFields/slug/index.js.map +1 -1
- package/dist/fields/validations.js +1 -1
- package/dist/fields/validations.js.map +1 -1
- package/dist/fields/validations.spec.js +25 -0
- package/dist/fields/validations.spec.js.map +1 -1
- package/dist/globals/operations/update.d.ts.map +1 -1
- package/dist/globals/operations/update.js +2 -1
- package/dist/globals/operations/update.js.map +1 -1
- package/dist/index.bundled.d.ts +16 -13
- package/dist/index.d.ts +1 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -10
- package/dist/index.js.map +1 -1
- package/dist/queues/localAPI.d.ts +0 -1
- package/dist/queues/localAPI.d.ts.map +1 -1
- package/dist/queues/localAPI.js +0 -4
- package/dist/queues/localAPI.js.map +1 -1
- package/dist/queues/operations/runJobs/index.d.ts +0 -1
- package/dist/queues/operations/runJobs/index.d.ts.map +1 -1
- package/dist/queues/operations/runJobs/index.js +1 -96
- package/dist/queues/operations/runJobs/index.js.map +1 -1
- package/dist/queues/utilities/updateJob.d.ts +1 -2
- package/dist/queues/utilities/updateJob.d.ts.map +1 -1
- package/dist/queues/utilities/updateJob.js +31 -92
- package/dist/queues/utilities/updateJob.js.map +1 -1
- package/dist/types/constants.d.ts +5 -0
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/constants.js +4 -0
- package/dist/types/constants.js.map +1 -1
- package/dist/uploads/endpoints/getFile.d.ts.map +1 -1
- package/dist/uploads/endpoints/getFile.js +7 -1
- package/dist/uploads/endpoints/getFile.js.map +1 -1
- package/dist/uploads/endpoints/getFileFromURL.d.ts.map +1 -1
- package/dist/uploads/endpoints/getFileFromURL.js +67 -28
- package/dist/uploads/endpoints/getFileFromURL.js.map +1 -1
- package/dist/uploads/getExternalFile.d.ts.map +1 -1
- package/dist/uploads/getExternalFile.js +3 -0
- package/dist/uploads/getExternalFile.js.map +1 -1
- package/dist/uploads/safeFetch.d.ts +1 -1
- package/dist/uploads/safeFetch.d.ts.map +1 -1
- package/dist/uploads/safeFetch.js.map +1 -1
- package/dist/utilities/addDataAndFileToRequest.d.ts.map +1 -1
- package/dist/utilities/addDataAndFileToRequest.js +7 -1
- package/dist/utilities/addDataAndFileToRequest.js.map +1 -1
- package/dist/utilities/configToJSONSchema.d.ts +7 -3
- package/dist/utilities/configToJSONSchema.d.ts.map +1 -1
- package/dist/utilities/configToJSONSchema.js +23 -33
- package/dist/utilities/configToJSONSchema.js.map +1 -1
- package/dist/utilities/configToJSONSchema.spec.js +75 -1
- package/dist/utilities/configToJSONSchema.spec.js.map +1 -1
- package/dist/utilities/getRequestOrigin.d.ts +10 -0
- package/dist/utilities/getRequestOrigin.d.ts.map +1 -0
- package/dist/utilities/getRequestOrigin.js +50 -0
- package/dist/utilities/getRequestOrigin.js.map +1 -0
- package/dist/utilities/getRequestOrigin.spec.js +151 -0
- package/dist/utilities/getRequestOrigin.spec.js.map +1 -0
- package/dist/utilities/sanitizeUrl.d.ts +7 -0
- package/dist/utilities/sanitizeUrl.d.ts.map +1 -0
- package/dist/utilities/sanitizeUrl.js +28 -0
- package/dist/utilities/sanitizeUrl.js.map +1 -0
- package/dist/versions/saveVersion.d.ts +1 -0
- package/dist/versions/saveVersion.d.ts.map +1 -1
- package/dist/versions/saveVersion.js +16 -66
- package/dist/versions/saveVersion.js.map +1 -1
- package/dist/versions/updateLatestVersion.d.ts +24 -0
- package/dist/versions/updateLatestVersion.d.ts.map +1 -0
- package/dist/versions/updateLatestVersion.js +64 -0
- package/dist/versions/updateLatestVersion.js.map +1 -0
- package/package.json +4 -4
|
@@ -471,7 +471,9 @@ describe('configToJSONSchema', ()=>{
|
|
|
471
471
|
],
|
|
472
472
|
items: {
|
|
473
473
|
oneOf: [
|
|
474
|
-
|
|
474
|
+
{
|
|
475
|
+
$ref: '#/definitions/SharedBlock'
|
|
476
|
+
}
|
|
475
477
|
]
|
|
476
478
|
}
|
|
477
479
|
}
|
|
@@ -512,6 +514,78 @@ describe('configToJSONSchema', ()=>{
|
|
|
512
514
|
// @ts-expect-error
|
|
513
515
|
expect(schema.definitions.test.properties.title.required).toStrictEqual(false);
|
|
514
516
|
});
|
|
517
|
+
it('should propagate forceInlineBlocks to nested fields (array, group, tab)', async ()=>{
|
|
518
|
+
const namedBlock = {
|
|
519
|
+
slug: 'myBlock',
|
|
520
|
+
interfaceName: 'MyBlock',
|
|
521
|
+
fields: [
|
|
522
|
+
{
|
|
523
|
+
name: 'text',
|
|
524
|
+
type: 'text'
|
|
525
|
+
}
|
|
526
|
+
]
|
|
527
|
+
};
|
|
528
|
+
// @ts-expect-error
|
|
529
|
+
const config = {
|
|
530
|
+
collections: [
|
|
531
|
+
{
|
|
532
|
+
slug: 'test',
|
|
533
|
+
fields: [
|
|
534
|
+
{
|
|
535
|
+
name: 'arr',
|
|
536
|
+
type: 'array',
|
|
537
|
+
fields: [
|
|
538
|
+
{
|
|
539
|
+
name: 'blocks',
|
|
540
|
+
type: 'blocks',
|
|
541
|
+
blocks: [
|
|
542
|
+
namedBlock
|
|
543
|
+
]
|
|
544
|
+
}
|
|
545
|
+
]
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: 'grp',
|
|
549
|
+
type: 'group',
|
|
550
|
+
fields: [
|
|
551
|
+
{
|
|
552
|
+
name: 'blocks',
|
|
553
|
+
type: 'blocks',
|
|
554
|
+
blocks: [
|
|
555
|
+
namedBlock
|
|
556
|
+
]
|
|
557
|
+
}
|
|
558
|
+
]
|
|
559
|
+
}
|
|
560
|
+
],
|
|
561
|
+
timestamps: false
|
|
562
|
+
}
|
|
563
|
+
]
|
|
564
|
+
};
|
|
565
|
+
const sanitizedConfig = await sanitizeConfig(config);
|
|
566
|
+
// Without forceInlineBlocks: blocks field uses $ref
|
|
567
|
+
const schemaDefault = configToJSONSchema(sanitizedConfig, 'text');
|
|
568
|
+
const arrItemsDefault = schemaDefault.definitions.test.properties.arr.items;
|
|
569
|
+
const arrBlocksDefault = arrItemsDefault.properties.blocks.items.oneOf[0];
|
|
570
|
+
expect(arrBlocksDefault).toStrictEqual({
|
|
571
|
+
$ref: '#/definitions/MyBlock'
|
|
572
|
+
});
|
|
573
|
+
// With forceInlineBlocks: blocks field is inlined, no $ref
|
|
574
|
+
const schemaInline = configToJSONSchema(sanitizedConfig, 'text', undefined, {
|
|
575
|
+
forceInlineBlocks: true
|
|
576
|
+
});
|
|
577
|
+
const arrItemsInline = schemaInline.definitions.test.properties.arr.items;
|
|
578
|
+
const arrBlocksInline = arrItemsInline.properties.blocks.items.oneOf[0];
|
|
579
|
+
expect(arrBlocksInline).not.toHaveProperty('$ref');
|
|
580
|
+
expect(arrBlocksInline.properties?.blockType).toStrictEqual({
|
|
581
|
+
const: 'myBlock'
|
|
582
|
+
});
|
|
583
|
+
const grpBlocksInline = schemaInline.definitions.test.properties.grp.properties.blocks.items.oneOf[0];
|
|
584
|
+
expect(grpBlocksInline).not.toHaveProperty('$ref');
|
|
585
|
+
expect(grpBlocksInline.properties?.blockType).toStrictEqual({
|
|
586
|
+
const: 'myBlock'
|
|
587
|
+
});
|
|
588
|
+
});
|
|
515
589
|
});
|
|
516
590
|
|
|
517
591
|
//# sourceMappingURL=configToJSONSchema.spec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/configToJSONSchema.spec.ts"],"sourcesContent":["import type { JSONSchema4 } from 'json-schema'\nimport { describe, it, expect } from 'vitest'\n\nimport type { Config } from '../config/types.js'\n\nimport { sanitizeConfig } from '../config/sanitize.js'\nimport { configToJSONSchema } from './configToJSONSchema.js'\nimport type { Block, BlocksField, RichTextField } from '../fields/config/types.js'\n\ndescribe('configToJSONSchema', () => {\n it('should handle optional arrays with required fields', async () => {\n // @ts-expect-error\n const config: Config = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'someRequiredField',\n type: 'array',\n fields: [\n {\n name: 'someRequiredField',\n type: 'text',\n required: true,\n },\n ],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config)\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n expect(schema?.definitions?.test).toStrictEqual({\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string',\n },\n someRequiredField: {\n type: ['array', 'null'],\n items: {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: ['string', 'null'],\n },\n someRequiredField: {\n type: 'string',\n },\n },\n required: ['someRequiredField'],\n },\n },\n },\n required: ['id'],\n title: 'Test',\n })\n })\n\n it('should handle block fields with no blocks', async () => {\n // @ts-expect-error\n const config: Config = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'blockField',\n type: 'blocks',\n blocks: [],\n },\n {\n name: 'blockFieldRequired',\n type: 'blocks',\n blocks: [],\n required: true,\n },\n {\n name: 'blockFieldWithFields',\n type: 'blocks',\n blocks: [\n {\n slug: 'test',\n fields: [\n {\n name: 'field',\n type: 'text',\n },\n ],\n },\n ],\n },\n {\n name: 'blockFieldWithFieldsRequired',\n type: 'blocks',\n blocks: [\n {\n slug: 'test',\n fields: [\n {\n name: 'field',\n type: 'text',\n required: true,\n },\n ],\n },\n ],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config)\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n expect(schema?.definitions?.test).toStrictEqual({\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string',\n },\n blockField: {\n type: ['array', 'null'],\n items: {},\n },\n blockFieldRequired: {\n type: 'array',\n items: {},\n },\n blockFieldWithFields: {\n type: ['array', 'null'],\n items: {\n oneOf: [\n {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: ['string', 'null'],\n },\n blockName: {\n type: ['string', 'null'],\n },\n blockType: {\n const: 'test',\n },\n field: {\n type: ['string', 'null'],\n },\n },\n required: ['blockType'],\n },\n ],\n },\n },\n blockFieldWithFieldsRequired: {\n type: ['array', 'null'],\n items: {\n oneOf: [\n {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: ['string', 'null'],\n },\n blockName: {\n type: ['string', 'null'],\n },\n blockType: {\n const: 'test',\n },\n field: {\n type: 'string',\n },\n },\n required: ['blockType', 'field'],\n },\n ],\n },\n },\n },\n required: ['id', 'blockFieldRequired'],\n title: 'Test',\n })\n })\n\n it('should handle tabs and named tabs with required fields', async () => {\n // @ts-expect-error\n const config: Config = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n type: 'tabs',\n tabs: [\n {\n fields: [\n {\n name: 'fieldInUnnamedTab',\n type: 'text',\n },\n ],\n label: 'unnamedTab',\n },\n {\n name: 'namedTab',\n fields: [\n {\n name: 'fieldInNamedTab',\n type: 'text',\n },\n ],\n label: 'namedTab',\n },\n {\n name: 'namedTabWithRequired',\n fields: [\n {\n name: 'fieldInNamedTab',\n type: 'text',\n required: true,\n },\n ],\n label: 'namedTabWithRequired',\n },\n ],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config)\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n expect(schema?.definitions?.test).toStrictEqual({\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string',\n },\n fieldInUnnamedTab: {\n type: ['string', 'null'],\n },\n namedTab: {\n type: 'object',\n additionalProperties: false,\n properties: {\n fieldInNamedTab: {\n type: ['string', 'null'],\n },\n },\n required: [],\n },\n namedTabWithRequired: {\n type: 'object',\n additionalProperties: false,\n properties: {\n fieldInNamedTab: {\n type: 'string',\n },\n },\n required: ['fieldInNamedTab'],\n },\n },\n required: ['id', 'namedTabWithRequired'],\n title: 'Test',\n })\n })\n\n it('should handle custom typescript schema and JSON field schema', async () => {\n const customSchema: JSONSchema4 = {\n type: 'object',\n properties: {\n id: {\n type: 'number',\n },\n required: ['id'],\n },\n }\n\n const config: Partial<Config> = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'withCustom',\n type: 'text',\n typescriptSchema: [() => customSchema],\n },\n {\n name: 'jsonWithSchema',\n type: 'json',\n jsonSchema: {\n fileMatch: ['a://b/foo.json'],\n schema: customSchema,\n uri: 'a://b/foo.json',\n },\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config as Config)\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n expect(schema?.definitions?.test).toStrictEqual({\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string',\n },\n jsonWithSchema: customSchema,\n withCustom: customSchema,\n },\n required: ['id'],\n title: 'Test',\n })\n })\n\n it('should handle same block object being referenced in both collection and config.blocks', async () => {\n const sharedBlock: Block = {\n slug: 'sharedBlock',\n interfaceName: 'SharedBlock',\n fields: [\n {\n name: 'richText',\n type: 'richText',\n editor: () => {\n // stub rich text editor\n return {\n CellComponent: '',\n FieldComponent: '',\n validate: () => true,\n }\n },\n },\n ],\n }\n\n // @ts-expect-error\n const config: Config = {\n blocks: [sharedBlock],\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'someBlockField',\n type: 'blocks',\n blocks: [sharedBlock],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n // Ensure both rich text editor are sanitized\n const sanitizedConfig = await sanitizeConfig(config)\n expect(typeof (sanitizedConfig?.blocks?.[0]?.fields?.[0] as RichTextField)?.editor).toBe(\n 'object',\n )\n expect(\n typeof (\n (sanitizedConfig.collections[0].fields[0] as BlocksField)?.blocks?.[0]\n ?.fields?.[0] as RichTextField\n )?.editor,\n ).toBe('object')\n\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n const expectedBlockSchema = {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: { type: ['string', 'null'] },\n blockName: { type: ['string', 'null'] },\n blockType: { const: 'sharedBlock' },\n richText: { type: ['array', 'null'], items: { type: 'object' } },\n },\n required: ['blockType'],\n }\n\n expect(schema?.definitions?.test).toStrictEqual({\n type: 'object',\n additionalProperties: false,\n title: 'Test',\n properties: {\n id: {\n type: 'string',\n },\n someBlockField: {\n type: ['array', 'null'],\n items: {\n oneOf: [expectedBlockSchema],\n },\n },\n },\n required: ['id'],\n })\n\n // The definition should still be registered for TypeScript type generation\n expect(schema?.definitions?.SharedBlock).toStrictEqual(expectedBlockSchema)\n })\n\n it('should allow overriding required to false', async () => {\n // @ts-expect-error\n const config: Config = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'title',\n type: 'text',\n required: true,\n defaultValue: 'test',\n typescriptSchema: [\n () => ({\n type: 'string',\n required: false,\n }),\n ],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config)\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n // @ts-expect-error\n expect(schema.definitions.test.properties.title.required).toStrictEqual(false)\n })\n})\n"],"names":["describe","it","expect","sanitizeConfig","configToJSONSchema","config","collections","slug","fields","name","type","required","timestamps","sanitizedConfig","schema","definitions","test","toStrictEqual","additionalProperties","properties","id","someRequiredField","items","title","blocks","blockField","blockFieldRequired","blockFieldWithFields","oneOf","blockName","blockType","const","field","blockFieldWithFieldsRequired","tabs","label","fieldInUnnamedTab","namedTab","fieldInNamedTab","namedTabWithRequired","customSchema","typescriptSchema","jsonSchema","fileMatch","uri","jsonWithSchema","withCustom","sharedBlock","interfaceName","editor","CellComponent","FieldComponent","validate","toBe","expectedBlockSchema","richText","someBlockField","SharedBlock","defaultValue"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/configToJSONSchema.spec.ts"],"sourcesContent":["import type { JSONSchema4 } from 'json-schema'\nimport { describe, it, expect } from 'vitest'\n\nimport type { Config } from '../config/types.js'\n\nimport { sanitizeConfig } from '../config/sanitize.js'\nimport { configToJSONSchema } from './configToJSONSchema.js'\nimport type { Block, BlocksField, RichTextField } from '../fields/config/types.js'\n\ndescribe('configToJSONSchema', () => {\n it('should handle optional arrays with required fields', async () => {\n // @ts-expect-error\n const config: Config = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'someRequiredField',\n type: 'array',\n fields: [\n {\n name: 'someRequiredField',\n type: 'text',\n required: true,\n },\n ],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config)\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n expect(schema?.definitions?.test).toStrictEqual({\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string',\n },\n someRequiredField: {\n type: ['array', 'null'],\n items: {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: ['string', 'null'],\n },\n someRequiredField: {\n type: 'string',\n },\n },\n required: ['someRequiredField'],\n },\n },\n },\n required: ['id'],\n title: 'Test',\n })\n })\n\n it('should handle block fields with no blocks', async () => {\n // @ts-expect-error\n const config: Config = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'blockField',\n type: 'blocks',\n blocks: [],\n },\n {\n name: 'blockFieldRequired',\n type: 'blocks',\n blocks: [],\n required: true,\n },\n {\n name: 'blockFieldWithFields',\n type: 'blocks',\n blocks: [\n {\n slug: 'test',\n fields: [\n {\n name: 'field',\n type: 'text',\n },\n ],\n },\n ],\n },\n {\n name: 'blockFieldWithFieldsRequired',\n type: 'blocks',\n blocks: [\n {\n slug: 'test',\n fields: [\n {\n name: 'field',\n type: 'text',\n required: true,\n },\n ],\n },\n ],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config)\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n expect(schema?.definitions?.test).toStrictEqual({\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string',\n },\n blockField: {\n type: ['array', 'null'],\n items: {},\n },\n blockFieldRequired: {\n type: 'array',\n items: {},\n },\n blockFieldWithFields: {\n type: ['array', 'null'],\n items: {\n oneOf: [\n {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: ['string', 'null'],\n },\n blockName: {\n type: ['string', 'null'],\n },\n blockType: {\n const: 'test',\n },\n field: {\n type: ['string', 'null'],\n },\n },\n required: ['blockType'],\n },\n ],\n },\n },\n blockFieldWithFieldsRequired: {\n type: ['array', 'null'],\n items: {\n oneOf: [\n {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: ['string', 'null'],\n },\n blockName: {\n type: ['string', 'null'],\n },\n blockType: {\n const: 'test',\n },\n field: {\n type: 'string',\n },\n },\n required: ['blockType', 'field'],\n },\n ],\n },\n },\n },\n required: ['id', 'blockFieldRequired'],\n title: 'Test',\n })\n })\n\n it('should handle tabs and named tabs with required fields', async () => {\n // @ts-expect-error\n const config: Config = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n type: 'tabs',\n tabs: [\n {\n fields: [\n {\n name: 'fieldInUnnamedTab',\n type: 'text',\n },\n ],\n label: 'unnamedTab',\n },\n {\n name: 'namedTab',\n fields: [\n {\n name: 'fieldInNamedTab',\n type: 'text',\n },\n ],\n label: 'namedTab',\n },\n {\n name: 'namedTabWithRequired',\n fields: [\n {\n name: 'fieldInNamedTab',\n type: 'text',\n required: true,\n },\n ],\n label: 'namedTabWithRequired',\n },\n ],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config)\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n expect(schema?.definitions?.test).toStrictEqual({\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string',\n },\n fieldInUnnamedTab: {\n type: ['string', 'null'],\n },\n namedTab: {\n type: 'object',\n additionalProperties: false,\n properties: {\n fieldInNamedTab: {\n type: ['string', 'null'],\n },\n },\n required: [],\n },\n namedTabWithRequired: {\n type: 'object',\n additionalProperties: false,\n properties: {\n fieldInNamedTab: {\n type: 'string',\n },\n },\n required: ['fieldInNamedTab'],\n },\n },\n required: ['id', 'namedTabWithRequired'],\n title: 'Test',\n })\n })\n\n it('should handle custom typescript schema and JSON field schema', async () => {\n const customSchema: JSONSchema4 = {\n type: 'object',\n properties: {\n id: {\n type: 'number',\n },\n required: ['id'],\n },\n }\n\n const config: Partial<Config> = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'withCustom',\n type: 'text',\n typescriptSchema: [() => customSchema],\n },\n {\n name: 'jsonWithSchema',\n type: 'json',\n jsonSchema: {\n fileMatch: ['a://b/foo.json'],\n schema: customSchema,\n uri: 'a://b/foo.json',\n },\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config as Config)\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n expect(schema?.definitions?.test).toStrictEqual({\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string',\n },\n jsonWithSchema: customSchema,\n withCustom: customSchema,\n },\n required: ['id'],\n title: 'Test',\n })\n })\n\n it('should handle same block object being referenced in both collection and config.blocks', async () => {\n const sharedBlock: Block = {\n slug: 'sharedBlock',\n interfaceName: 'SharedBlock',\n fields: [\n {\n name: 'richText',\n type: 'richText',\n editor: () => {\n // stub rich text editor\n return {\n CellComponent: '',\n FieldComponent: '',\n validate: () => true,\n }\n },\n },\n ],\n }\n\n // @ts-expect-error\n const config: Config = {\n blocks: [sharedBlock],\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'someBlockField',\n type: 'blocks',\n blocks: [sharedBlock],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n // Ensure both rich text editor are sanitized\n const sanitizedConfig = await sanitizeConfig(config)\n expect(typeof (sanitizedConfig?.blocks?.[0]?.fields?.[0] as RichTextField)?.editor).toBe(\n 'object',\n )\n expect(\n typeof (\n (sanitizedConfig.collections[0].fields[0] as BlocksField)?.blocks?.[0]\n ?.fields?.[0] as RichTextField\n )?.editor,\n ).toBe('object')\n\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n const expectedBlockSchema = {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: { type: ['string', 'null'] },\n blockName: { type: ['string', 'null'] },\n blockType: { const: 'sharedBlock' },\n richText: { type: ['array', 'null'], items: { type: 'object' } },\n },\n required: ['blockType'],\n }\n\n expect(schema?.definitions?.test).toStrictEqual({\n type: 'object',\n additionalProperties: false,\n title: 'Test',\n properties: {\n id: {\n type: 'string',\n },\n someBlockField: {\n type: ['array', 'null'],\n items: {\n oneOf: [\n {\n $ref: '#/definitions/SharedBlock',\n },\n ],\n },\n },\n },\n required: ['id'],\n })\n\n // The definition should still be registered for TypeScript type generation\n expect(schema?.definitions?.SharedBlock).toStrictEqual(expectedBlockSchema)\n })\n\n it('should allow overriding required to false', async () => {\n // @ts-expect-error\n const config: Config = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'title',\n type: 'text',\n required: true,\n defaultValue: 'test',\n typescriptSchema: [\n () => ({\n type: 'string',\n required: false,\n }),\n ],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config)\n const schema = configToJSONSchema(sanitizedConfig, 'text')\n\n // @ts-expect-error\n expect(schema.definitions.test.properties.title.required).toStrictEqual(false)\n })\n\n it('should propagate forceInlineBlocks to nested fields (array, group, tab)', async () => {\n const namedBlock: Block = {\n slug: 'myBlock',\n interfaceName: 'MyBlock',\n fields: [{ name: 'text', type: 'text' }],\n }\n\n // @ts-expect-error\n const config: Config = {\n collections: [\n {\n slug: 'test',\n fields: [\n {\n name: 'arr',\n type: 'array',\n fields: [{ name: 'blocks', type: 'blocks', blocks: [namedBlock] }],\n },\n {\n name: 'grp',\n type: 'group',\n fields: [{ name: 'blocks', type: 'blocks', blocks: [namedBlock] }],\n },\n ],\n timestamps: false,\n },\n ],\n }\n\n const sanitizedConfig = await sanitizeConfig(config)\n\n // Without forceInlineBlocks: blocks field uses $ref\n const schemaDefault = configToJSONSchema(sanitizedConfig, 'text')\n const arrItemsDefault = schemaDefault.definitions!.test.properties!.arr.items as JSONSchema4\n const arrBlocksDefault = (arrItemsDefault.properties!.blocks.items as JSONSchema4).oneOf![0]\n expect(arrBlocksDefault).toStrictEqual({ $ref: '#/definitions/MyBlock' })\n\n // With forceInlineBlocks: blocks field is inlined, no $ref\n const schemaInline = configToJSONSchema(sanitizedConfig, 'text', undefined, {\n forceInlineBlocks: true,\n })\n const arrItemsInline = schemaInline.definitions!.test.properties!.arr.items as JSONSchema4\n const arrBlocksInline = (arrItemsInline.properties!.blocks.items as JSONSchema4).oneOf![0]\n expect(arrBlocksInline).not.toHaveProperty('$ref')\n expect(arrBlocksInline.properties?.blockType).toStrictEqual({ const: 'myBlock' })\n\n const grpBlocksInline = (\n schemaInline.definitions!.test.properties!.grp.properties!.blocks.items as JSONSchema4\n ).oneOf![0]\n expect(grpBlocksInline).not.toHaveProperty('$ref')\n expect(grpBlocksInline.properties?.blockType).toStrictEqual({ const: 'myBlock' })\n })\n})\n"],"names":["describe","it","expect","sanitizeConfig","configToJSONSchema","config","collections","slug","fields","name","type","required","timestamps","sanitizedConfig","schema","definitions","test","toStrictEqual","additionalProperties","properties","id","someRequiredField","items","title","blocks","blockField","blockFieldRequired","blockFieldWithFields","oneOf","blockName","blockType","const","field","blockFieldWithFieldsRequired","tabs","label","fieldInUnnamedTab","namedTab","fieldInNamedTab","namedTabWithRequired","customSchema","typescriptSchema","jsonSchema","fileMatch","uri","jsonWithSchema","withCustom","sharedBlock","interfaceName","editor","CellComponent","FieldComponent","validate","toBe","expectedBlockSchema","richText","someBlockField","$ref","SharedBlock","defaultValue","namedBlock","schemaDefault","arrItemsDefault","arr","arrBlocksDefault","schemaInline","undefined","forceInlineBlocks","arrItemsInline","arrBlocksInline","not","toHaveProperty","grpBlocksInline","grp"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SanitizedConfig } from '../config/types.js';
|
|
2
|
+
import type { PayloadRequest } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Returns a trusted request origin
|
|
5
|
+
*/
|
|
6
|
+
export declare const getRequestOrigin: ({ config, req, }: {
|
|
7
|
+
config: Pick<SanitizedConfig, "cors" | "csrf" | "serverURL">;
|
|
8
|
+
req: Pick<PayloadRequest, "headers" | "payload" | "url">;
|
|
9
|
+
}) => string;
|
|
10
|
+
//# sourceMappingURL=getRequestOrigin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRequestOrigin.d.ts","sourceRoot":"","sources":["../../src/utilities/getRequestOrigin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AA8BvD;;GAEG;AACH,eAAO,MAAM,gBAAgB,qBAG1B;IACD,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC,CAAA;IAC5D,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,SAAS,GAAG,KAAK,CAAC,CAAA;CACzD,KAAG,MA4BH,CAAA"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const getTrustedOrigins = (config)=>{
|
|
2
|
+
const origins = new Set();
|
|
3
|
+
const { cors, csrf } = config;
|
|
4
|
+
if (cors === '*') {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
if (Array.isArray(cors)) {
|
|
8
|
+
cors.forEach((o)=>origins.add(o));
|
|
9
|
+
} else if (cors && typeof cors === 'object') {
|
|
10
|
+
const corsOrigins = cors.origins;
|
|
11
|
+
if (corsOrigins === '*') {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(corsOrigins)) {
|
|
15
|
+
corsOrigins.forEach((o)=>origins.add(o));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(csrf)) {
|
|
19
|
+
csrf.forEach((o)=>origins.add(o));
|
|
20
|
+
}
|
|
21
|
+
return [
|
|
22
|
+
...origins
|
|
23
|
+
];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Returns a trusted request origin
|
|
27
|
+
*/ export const getRequestOrigin = ({ config, req })=>{
|
|
28
|
+
if (config.serverURL !== null && config.serverURL !== '') {
|
|
29
|
+
return config.serverURL;
|
|
30
|
+
}
|
|
31
|
+
let origin = '';
|
|
32
|
+
try {
|
|
33
|
+
const protocol = new URL(req.url).protocol;
|
|
34
|
+
const host = req.headers?.get('host');
|
|
35
|
+
if (host) {
|
|
36
|
+
origin = `${protocol}//${host}`;
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// req.url is malformed; origin stays empty
|
|
40
|
+
}
|
|
41
|
+
const trustedOrigins = getTrustedOrigins(config);
|
|
42
|
+
if (trustedOrigins !== null && origin && trustedOrigins.includes(origin)) {
|
|
43
|
+
// Host header value is explicitly listed in the CORS/CSRF allowlist — safe to use.
|
|
44
|
+
return origin;
|
|
45
|
+
}
|
|
46
|
+
req.payload.logger.warn(`Request origin "${origin}" is not in the CORS/CSRF allowlist. Falling back to empty string as request origin. It is recommended to explicitly set the serverURL in the config to avoid this warning and ensure correct request origin is used.`);
|
|
47
|
+
return '';
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=getRequestOrigin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/getRequestOrigin.ts"],"sourcesContent":["import type { CORSConfig, SanitizedConfig } from '../config/types.js'\nimport type { PayloadRequest } from '../types/index.js'\n\nconst getTrustedOrigins = (config: Pick<SanitizedConfig, 'cors' | 'csrf'>): null | string[] => {\n const origins = new Set<string>()\n\n const { cors, csrf } = config\n\n if (cors === '*') {\n return null\n }\n\n if (Array.isArray(cors)) {\n cors.forEach((o) => origins.add(o))\n } else if (cors && typeof cors === 'object') {\n const corsOrigins = (cors as CORSConfig).origins\n if (corsOrigins === '*') {\n return null\n }\n if (Array.isArray(corsOrigins)) {\n corsOrigins.forEach((o) => origins.add(o))\n }\n }\n\n if (Array.isArray(csrf)) {\n csrf.forEach((o) => origins.add(o))\n }\n\n return [...origins]\n}\n\n/**\n * Returns a trusted request origin\n */\nexport const getRequestOrigin = ({\n config,\n req,\n}: {\n config: Pick<SanitizedConfig, 'cors' | 'csrf' | 'serverURL'>\n req: Pick<PayloadRequest, 'headers' | 'payload' | 'url'>\n}): string => {\n if (config.serverURL !== null && config.serverURL !== '') {\n return config.serverURL\n }\n\n let origin = ''\n try {\n const protocol = new URL(req.url!).protocol\n const host = req.headers?.get('host')\n if (host) {\n origin = `${protocol}//${host}`\n }\n } catch {\n // req.url is malformed; origin stays empty\n }\n\n const trustedOrigins = getTrustedOrigins(config)\n\n if (trustedOrigins !== null && origin && trustedOrigins.includes(origin)) {\n // Host header value is explicitly listed in the CORS/CSRF allowlist — safe to use.\n return origin\n }\n\n req.payload.logger.warn(\n `Request origin \"${origin}\" is not in the CORS/CSRF allowlist. Falling back to empty string as request origin. It is recommended to explicitly set the serverURL in the config to avoid this warning and ensure correct request origin is used.`,\n )\n\n return ''\n}\n"],"names":["getTrustedOrigins","config","origins","Set","cors","csrf","Array","isArray","forEach","o","add","corsOrigins","getRequestOrigin","req","serverURL","origin","protocol","URL","url","host","headers","get","trustedOrigins","includes","payload","logger","warn"],"mappings":"AAGA,MAAMA,oBAAoB,CAACC;IACzB,MAAMC,UAAU,IAAIC;IAEpB,MAAM,EAAEC,IAAI,EAAEC,IAAI,EAAE,GAAGJ;IAEvB,IAAIG,SAAS,KAAK;QAChB,OAAO;IACT;IAEA,IAAIE,MAAMC,OAAO,CAACH,OAAO;QACvBA,KAAKI,OAAO,CAAC,CAACC,IAAMP,QAAQQ,GAAG,CAACD;IAClC,OAAO,IAAIL,QAAQ,OAAOA,SAAS,UAAU;QAC3C,MAAMO,cAAc,AAACP,KAAoBF,OAAO;QAChD,IAAIS,gBAAgB,KAAK;YACvB,OAAO;QACT;QACA,IAAIL,MAAMC,OAAO,CAACI,cAAc;YAC9BA,YAAYH,OAAO,CAAC,CAACC,IAAMP,QAAQQ,GAAG,CAACD;QACzC;IACF;IAEA,IAAIH,MAAMC,OAAO,CAACF,OAAO;QACvBA,KAAKG,OAAO,CAAC,CAACC,IAAMP,QAAQQ,GAAG,CAACD;IAClC;IAEA,OAAO;WAAIP;KAAQ;AACrB;AAEA;;CAEC,GACD,OAAO,MAAMU,mBAAmB,CAAC,EAC/BX,MAAM,EACNY,GAAG,EAIJ;IACC,IAAIZ,OAAOa,SAAS,KAAK,QAAQb,OAAOa,SAAS,KAAK,IAAI;QACxD,OAAOb,OAAOa,SAAS;IACzB;IAEA,IAAIC,SAAS;IACb,IAAI;QACF,MAAMC,WAAW,IAAIC,IAAIJ,IAAIK,GAAG,EAAGF,QAAQ;QAC3C,MAAMG,OAAON,IAAIO,OAAO,EAAEC,IAAI;QAC9B,IAAIF,MAAM;YACRJ,SAAS,GAAGC,SAAS,EAAE,EAAEG,MAAM;QACjC;IACF,EAAE,OAAM;IACN,2CAA2C;IAC7C;IAEA,MAAMG,iBAAiBtB,kBAAkBC;IAEzC,IAAIqB,mBAAmB,QAAQP,UAAUO,eAAeC,QAAQ,CAACR,SAAS;QACxE,mFAAmF;QACnF,OAAOA;IACT;IAEAF,IAAIW,OAAO,CAACC,MAAM,CAACC,IAAI,CACrB,CAAC,gBAAgB,EAAEX,OAAO,qNAAqN,CAAC;IAGlP,OAAO;AACT,EAAC"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { getRequestOrigin } from './getRequestOrigin';
|
|
3
|
+
const makeReq = (url, hostOverride)=>{
|
|
4
|
+
let host = hostOverride;
|
|
5
|
+
if (host === undefined) {
|
|
6
|
+
try {
|
|
7
|
+
host = new URL(url).host;
|
|
8
|
+
} catch {
|
|
9
|
+
host = undefined;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
url,
|
|
14
|
+
headers: new Headers(host !== undefined ? {
|
|
15
|
+
host
|
|
16
|
+
} : {}),
|
|
17
|
+
payload: {
|
|
18
|
+
logger: {
|
|
19
|
+
warn: ()=>{}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
describe('getRequestOrigin', ()=>{
|
|
25
|
+
describe('when config.serverURL is set', ()=>{
|
|
26
|
+
it('should return serverURL regardless of Host header', ()=>{
|
|
27
|
+
const req = makeReq('https://ignored.com/api/forgot-password', 'attacker.com');
|
|
28
|
+
const result = getRequestOrigin({
|
|
29
|
+
config: {
|
|
30
|
+
serverURL: 'https://myapp.com',
|
|
31
|
+
cors: '*',
|
|
32
|
+
csrf: []
|
|
33
|
+
},
|
|
34
|
+
req
|
|
35
|
+
});
|
|
36
|
+
expect(result).toBe('https://myapp.com');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('when config.serverURL is not set', ()=>{
|
|
40
|
+
describe('CORS allowlist validation', ()=>{
|
|
41
|
+
it('should return origin when Host header matches a CORS string array entry', ()=>{
|
|
42
|
+
const req = makeReq('https://myapp.com/api/forgot-password');
|
|
43
|
+
const result = getRequestOrigin({
|
|
44
|
+
config: {
|
|
45
|
+
serverURL: '',
|
|
46
|
+
cors: [
|
|
47
|
+
'https://myapp.com',
|
|
48
|
+
'https://other.com'
|
|
49
|
+
],
|
|
50
|
+
csrf: []
|
|
51
|
+
},
|
|
52
|
+
req
|
|
53
|
+
});
|
|
54
|
+
expect(result).toBe('https://myapp.com');
|
|
55
|
+
});
|
|
56
|
+
it('should return origin when Host header matches a CORSConfig origins entry', ()=>{
|
|
57
|
+
const req = makeReq('https://myapp.com/api/verify');
|
|
58
|
+
const result = getRequestOrigin({
|
|
59
|
+
config: {
|
|
60
|
+
serverURL: '',
|
|
61
|
+
cors: {
|
|
62
|
+
headers: [],
|
|
63
|
+
origins: [
|
|
64
|
+
'https://myapp.com'
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
csrf: []
|
|
68
|
+
},
|
|
69
|
+
req
|
|
70
|
+
});
|
|
71
|
+
expect(result).toBe('https://myapp.com');
|
|
72
|
+
});
|
|
73
|
+
it('should return empty string when Host header is forged and not in CORS allowlist', ()=>{
|
|
74
|
+
const req = makeReq('https://myapp.com/api/forgot-password', 'attacker.com');
|
|
75
|
+
const result = getRequestOrigin({
|
|
76
|
+
config: {
|
|
77
|
+
serverURL: '',
|
|
78
|
+
cors: [
|
|
79
|
+
'https://myapp.com'
|
|
80
|
+
],
|
|
81
|
+
csrf: []
|
|
82
|
+
},
|
|
83
|
+
req
|
|
84
|
+
});
|
|
85
|
+
expect(result).toBe('');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('CSRF allowlist validation', ()=>{
|
|
89
|
+
it('should return origin when Host header matches a CSRF entry', ()=>{
|
|
90
|
+
const req = makeReq('https://myapp.com/api/verify');
|
|
91
|
+
const result = getRequestOrigin({
|
|
92
|
+
config: {
|
|
93
|
+
serverURL: '',
|
|
94
|
+
cors: [],
|
|
95
|
+
csrf: [
|
|
96
|
+
'https://myapp.com'
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
req
|
|
100
|
+
});
|
|
101
|
+
expect(result).toBe('https://myapp.com');
|
|
102
|
+
});
|
|
103
|
+
it('should return origin when Host header is in CSRF but not in CORS', ()=>{
|
|
104
|
+
const req = makeReq('https://myapp.com/api/verify');
|
|
105
|
+
const result = getRequestOrigin({
|
|
106
|
+
config: {
|
|
107
|
+
serverURL: '',
|
|
108
|
+
cors: [
|
|
109
|
+
'https://other.com'
|
|
110
|
+
],
|
|
111
|
+
csrf: [
|
|
112
|
+
'https://myapp.com'
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
req
|
|
116
|
+
});
|
|
117
|
+
expect(result).toBe('https://myapp.com');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe('malformed or missing Host header', ()=>{
|
|
121
|
+
it('should return empty string when req.url is not a valid URL', ()=>{
|
|
122
|
+
const req = makeReq('not-a-url');
|
|
123
|
+
const result = getRequestOrigin({
|
|
124
|
+
config: {
|
|
125
|
+
serverURL: '',
|
|
126
|
+
cors: [],
|
|
127
|
+
csrf: []
|
|
128
|
+
},
|
|
129
|
+
req
|
|
130
|
+
});
|
|
131
|
+
expect(result).toBe('');
|
|
132
|
+
});
|
|
133
|
+
it('should return empty string when Host header is absent', ()=>{
|
|
134
|
+
const req = makeReq('https://myapp.com/api/forgot-password', '');
|
|
135
|
+
const result = getRequestOrigin({
|
|
136
|
+
config: {
|
|
137
|
+
serverURL: '',
|
|
138
|
+
cors: [
|
|
139
|
+
'https://myapp.com'
|
|
140
|
+
],
|
|
141
|
+
csrf: []
|
|
142
|
+
},
|
|
143
|
+
req
|
|
144
|
+
});
|
|
145
|
+
expect(result).toBe('');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
//# sourceMappingURL=getRequestOrigin.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/getRequestOrigin.spec.ts"],"sourcesContent":["import type { PayloadRequest } from '../types/index.js'\n\nimport { describe, expect, it } from 'vitest'\n\nimport { getRequestOrigin } from './getRequestOrigin'\n\ntype MinimalReq = Pick<PayloadRequest, 'headers' | 'payload' | 'url'>\n\nconst makeReq = (url: string, hostOverride?: string): MinimalReq => {\n let host = hostOverride\n if (host === undefined) {\n try {\n host = new URL(url).host\n } catch {\n host = undefined\n }\n }\n return {\n url,\n headers: new Headers(host !== undefined ? { host } : {}),\n payload: {\n logger: {\n warn: () => {},\n },\n },\n } as unknown as MinimalReq\n}\n\ndescribe('getRequestOrigin', () => {\n describe('when config.serverURL is set', () => {\n it('should return serverURL regardless of Host header', () => {\n const req = makeReq('https://ignored.com/api/forgot-password', 'attacker.com')\n const result = getRequestOrigin({\n config: { serverURL: 'https://myapp.com', cors: '*', csrf: [] },\n req,\n })\n expect(result).toBe('https://myapp.com')\n })\n })\n\n describe('when config.serverURL is not set', () => {\n describe('CORS allowlist validation', () => {\n it('should return origin when Host header matches a CORS string array entry', () => {\n const req = makeReq('https://myapp.com/api/forgot-password')\n const result = getRequestOrigin({\n config: { serverURL: '', cors: ['https://myapp.com', 'https://other.com'], csrf: [] },\n req,\n })\n expect(result).toBe('https://myapp.com')\n })\n\n it('should return origin when Host header matches a CORSConfig origins entry', () => {\n const req = makeReq('https://myapp.com/api/verify')\n const result = getRequestOrigin({\n config: {\n serverURL: '',\n cors: { headers: [], origins: ['https://myapp.com'] },\n csrf: [],\n },\n req,\n })\n expect(result).toBe('https://myapp.com')\n })\n\n it('should return empty string when Host header is forged and not in CORS allowlist', () => {\n const req = makeReq('https://myapp.com/api/forgot-password', 'attacker.com')\n const result = getRequestOrigin({\n config: { serverURL: '', cors: ['https://myapp.com'], csrf: [] },\n req,\n })\n expect(result).toBe('')\n })\n })\n\n describe('CSRF allowlist validation', () => {\n it('should return origin when Host header matches a CSRF entry', () => {\n const req = makeReq('https://myapp.com/api/verify')\n const result = getRequestOrigin({\n config: { serverURL: '', cors: [], csrf: ['https://myapp.com'] },\n req,\n })\n expect(result).toBe('https://myapp.com')\n })\n\n it('should return origin when Host header is in CSRF but not in CORS', () => {\n const req = makeReq('https://myapp.com/api/verify')\n const result = getRequestOrigin({\n config: {\n serverURL: '',\n cors: ['https://other.com'],\n csrf: ['https://myapp.com'],\n },\n req,\n })\n expect(result).toBe('https://myapp.com')\n })\n })\n\n describe('malformed or missing Host header', () => {\n it('should return empty string when req.url is not a valid URL', () => {\n const req = makeReq('not-a-url')\n const result = getRequestOrigin({\n config: { serverURL: '', cors: [], csrf: [] },\n req,\n })\n expect(result).toBe('')\n })\n\n it('should return empty string when Host header is absent', () => {\n const req = makeReq('https://myapp.com/api/forgot-password', '')\n const result = getRequestOrigin({\n config: { serverURL: '', cors: ['https://myapp.com'], csrf: [] },\n req,\n })\n expect(result).toBe('')\n })\n })\n })\n})\n"],"names":["describe","expect","it","getRequestOrigin","makeReq","url","hostOverride","host","undefined","URL","headers","Headers","payload","logger","warn","req","result","config","serverURL","cors","csrf","toBe","origins"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitizes a URL to ensure only allowed protocols are used.
|
|
3
|
+
* Allows: http, https, mailto, tel, relative paths, and fragment (#) URLs.
|
|
4
|
+
* Returns '#' for any URL with a disallowed protocol (e.g. javascript:, data:).
|
|
5
|
+
*/
|
|
6
|
+
export declare function sanitizeUrl(url: string): string;
|
|
7
|
+
//# sourceMappingURL=sanitizeUrl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeUrl.d.ts","sourceRoot":"","sources":["../../src/utilities/sanitizeUrl.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgC/C"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitizes a URL to ensure only allowed protocols are used.
|
|
3
|
+
* Allows: http, https, mailto, tel, relative paths, and fragment (#) URLs.
|
|
4
|
+
* Returns '#' for any URL with a disallowed protocol (e.g. javascript:, data:).
|
|
5
|
+
*/ export function sanitizeUrl(url) {
|
|
6
|
+
if (!url) {
|
|
7
|
+
return '';
|
|
8
|
+
}
|
|
9
|
+
const trimmed = url.trim();
|
|
10
|
+
// eslint-disable-next-line no-control-regex
|
|
11
|
+
const cleaned = trimmed.replace(/[\x00-\x1f\x7f]/g, '');
|
|
12
|
+
if (cleaned.startsWith('#')) {
|
|
13
|
+
return cleaned;
|
|
14
|
+
}
|
|
15
|
+
if (cleaned.startsWith('/') || cleaned.startsWith('./') || cleaned.startsWith('../')) {
|
|
16
|
+
return cleaned;
|
|
17
|
+
}
|
|
18
|
+
const protocolMatch = cleaned.match(/^([a-z][a-z0-9+\-.]*):(?=.)/i);
|
|
19
|
+
if (protocolMatch) {
|
|
20
|
+
const protocol = protocolMatch[1].toLowerCase();
|
|
21
|
+
if (protocol !== 'http' && protocol !== 'https' && protocol !== 'mailto' && protocol !== 'tel') {
|
|
22
|
+
return '#';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return cleaned;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=sanitizeUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/sanitizeUrl.ts"],"sourcesContent":["/**\n * Sanitizes a URL to ensure only allowed protocols are used.\n * Allows: http, https, mailto, tel, relative paths, and fragment (#) URLs.\n * Returns '#' for any URL with a disallowed protocol (e.g. javascript:, data:).\n */\nexport function sanitizeUrl(url: string): string {\n if (!url) {\n return ''\n }\n\n const trimmed = url.trim()\n\n // eslint-disable-next-line no-control-regex\n const cleaned = trimmed.replace(/[\\x00-\\x1f\\x7f]/g, '')\n\n if (cleaned.startsWith('#')) {\n return cleaned\n }\n\n if (cleaned.startsWith('/') || cleaned.startsWith('./') || cleaned.startsWith('../')) {\n return cleaned\n }\n\n const protocolMatch = cleaned.match(/^([a-z][a-z0-9+\\-.]*):(?=.)/i)\n if (protocolMatch) {\n const protocol = protocolMatch[1]!.toLowerCase()\n if (\n protocol !== 'http' &&\n protocol !== 'https' &&\n protocol !== 'mailto' &&\n protocol !== 'tel'\n ) {\n return '#'\n }\n }\n\n return cleaned\n}\n"],"names":["sanitizeUrl","url","trimmed","trim","cleaned","replace","startsWith","protocolMatch","match","protocol","toLowerCase"],"mappings":"AAAA;;;;CAIC,GACD,OAAO,SAASA,YAAYC,GAAW;IACrC,IAAI,CAACA,KAAK;QACR,OAAO;IACT;IAEA,MAAMC,UAAUD,IAAIE,IAAI;IAExB,4CAA4C;IAC5C,MAAMC,UAAUF,QAAQG,OAAO,CAAC,oBAAoB;IAEpD,IAAID,QAAQE,UAAU,CAAC,MAAM;QAC3B,OAAOF;IACT;IAEA,IAAIA,QAAQE,UAAU,CAAC,QAAQF,QAAQE,UAAU,CAAC,SAASF,QAAQE,UAAU,CAAC,QAAQ;QACpF,OAAOF;IACT;IAEA,MAAMG,gBAAgBH,QAAQI,KAAK,CAAC;IACpC,IAAID,eAAe;QACjB,MAAME,WAAWF,aAAa,CAAC,EAAE,CAAEG,WAAW;QAC9C,IACED,aAAa,UACbA,aAAa,WACbA,aAAa,YACbA,aAAa,OACb;YACA,OAAO;QACT;IACF;IAEA,OAAOL;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"saveVersion.d.ts","sourceRoot":"","sources":["../../src/versions/saveVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAA;AAC/E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AACvE,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,aAAa,CAAA;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"saveVersion.d.ts","sourceRoot":"","sources":["../../src/versions/saveVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAA;AAC/E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AACvE,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,aAAa,CAAA;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAU/E,KAAK,IAAI,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,IAAI;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,yBAAyB,CAAA;IACtC,cAAc,EAAE,CAAC,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,qBAAqB,CAAA;IAC9B,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,QAAQ,CAAA;IAClD,OAAO,EAAE,OAAO,CAAA;IAChB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,GAAG,CAAC,EAAE,cAAc,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,QAAQ,CAAC,EAAE,GAAG,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,wBAAsB,WAAW,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU,EACrE,IAAI,EAAE;IAAE,SAAS,EAAE,KAAK,CAAA;CAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CAAA;AAChB,wBAAsB,WAAW,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU,EACrE,IAAI,EAAE;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GACtC,OAAO,CAAC,UAAU,CAAC,CAAA;AACtB,wBAAsB,WAAW,CAAC,KAAK,SAAS,UAAU,GAAG,UAAU,EACrE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,GACnC,OAAO,CAAC,UAAU,CAAC,CAAA"}
|
|
@@ -4,9 +4,10 @@ import { sanitizeInternalFields } from '../utilities/sanitizeInternalFields.js';
|
|
|
4
4
|
import { getQueryDraftsSelect } from './drafts/getQueryDraftsSelect.js';
|
|
5
5
|
import { enforceMaxVersions } from './enforceMaxVersions.js';
|
|
6
6
|
import { saveSnapshot } from './saveSnapshot.js';
|
|
7
|
-
|
|
7
|
+
import { updateLatestVersion } from './updateLatestVersion.js';
|
|
8
|
+
export async function saveVersion({ id, autosave, collection, docWithLocales, draft, global, operation, payload, publishSpecificLocale, req, returning, select, snapshot, unpublish }) {
|
|
8
9
|
let result;
|
|
9
|
-
let
|
|
10
|
+
let createdNewVersion = false;
|
|
10
11
|
const now = new Date().toISOString();
|
|
11
12
|
const versionData = deepCopyObjectSimple(docWithLocales);
|
|
12
13
|
if ((collection?.timestamps || global) && draft) {
|
|
@@ -16,71 +17,20 @@ export async function saveVersion({ id, autosave, collection, docWithLocales, dr
|
|
|
16
17
|
delete versionData._id;
|
|
17
18
|
}
|
|
18
19
|
try {
|
|
19
|
-
if (autosave) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
if (unpublish || autosave) {
|
|
21
|
+
result = await updateLatestVersion({
|
|
22
|
+
id,
|
|
23
|
+
collection,
|
|
24
|
+
global,
|
|
25
|
+
now,
|
|
26
|
+
payload,
|
|
24
27
|
req,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
;
|
|
29
|
-
({ docs } = await payload.db.findVersions({
|
|
30
|
-
...findVersionArgs,
|
|
31
|
-
collection: collection.slug,
|
|
32
|
-
limit: 1,
|
|
33
|
-
pagination: false,
|
|
34
|
-
req,
|
|
35
|
-
where: {
|
|
36
|
-
parent: {
|
|
37
|
-
equals: id
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}));
|
|
41
|
-
} else {
|
|
42
|
-
;
|
|
43
|
-
({ docs } = await payload.db.findGlobalVersions({
|
|
44
|
-
...findVersionArgs,
|
|
45
|
-
global: global.slug,
|
|
46
|
-
limit: 1,
|
|
47
|
-
pagination: false,
|
|
48
|
-
req
|
|
49
|
-
}));
|
|
50
|
-
}
|
|
51
|
-
const [latestVersion] = docs;
|
|
52
|
-
// overwrite the latest version if it's set to autosave
|
|
53
|
-
if (latestVersion && 'autosave' in latestVersion && latestVersion.autosave === true) {
|
|
54
|
-
createNewVersion = false;
|
|
55
|
-
const updateVersionArgs = {
|
|
56
|
-
id: latestVersion.id,
|
|
57
|
-
req,
|
|
58
|
-
versionData: {
|
|
59
|
-
createdAt: new Date(latestVersion.createdAt).toISOString(),
|
|
60
|
-
latest: true,
|
|
61
|
-
parent: id,
|
|
62
|
-
updatedAt: now,
|
|
63
|
-
version: {
|
|
64
|
-
...versionData
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
if (collection) {
|
|
69
|
-
result = await payload.db.updateVersion({
|
|
70
|
-
...updateVersionArgs,
|
|
71
|
-
collection: collection.slug,
|
|
72
|
-
req
|
|
73
|
-
});
|
|
74
|
-
} else {
|
|
75
|
-
result = await payload.db.updateGlobalVersion({
|
|
76
|
-
...updateVersionArgs,
|
|
77
|
-
global: global.slug,
|
|
78
|
-
req
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
28
|
+
shouldUpdate: autosave ? (v)=>'autosave' in v && v.autosave === true : undefined,
|
|
29
|
+
versionData
|
|
30
|
+
});
|
|
82
31
|
}
|
|
83
|
-
if (
|
|
32
|
+
if (!result) {
|
|
33
|
+
createdNewVersion = true;
|
|
84
34
|
const createVersionArgs = {
|
|
85
35
|
autosave: Boolean(autosave),
|
|
86
36
|
collectionSlug: undefined,
|
|
@@ -133,7 +83,7 @@ export async function saveVersion({ id, autosave, collection, docWithLocales, dr
|
|
|
133
83
|
return undefined;
|
|
134
84
|
}
|
|
135
85
|
const max = getVersionsMax(collection || global);
|
|
136
|
-
if (
|
|
86
|
+
if (createdNewVersion && max > 0) {
|
|
137
87
|
await enforceMaxVersions({
|
|
138
88
|
id,
|
|
139
89
|
collection,
|