@strapi/utils 5.36.1 → 5.37.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/content-api-constants.d.ts +17 -0
- package/dist/content-api-constants.d.ts.map +1 -0
- package/dist/content-api-constants.js +43 -0
- package/dist/content-api-constants.js.map +1 -0
- package/dist/content-api-constants.mjs +39 -0
- package/dist/content-api-constants.mjs.map +1 -0
- package/dist/content-api-route-params.d.ts +17 -0
- package/dist/content-api-route-params.d.ts.map +1 -0
- package/dist/content-api-route-params.js +21 -0
- package/dist/content-api-route-params.js.map +1 -0
- package/dist/content-api-route-params.mjs +18 -0
- package/dist/content-api-route-params.mjs.map +1 -0
- package/dist/content-types.d.ts +10 -2
- package/dist/content-types.d.ts.map +1 -1
- package/dist/content-types.js +22 -2
- package/dist/content-types.js.map +1 -1
- package/dist/content-types.mjs +19 -3
- package/dist/content-types.mjs.map +1 -1
- package/dist/convert-query-params.d.ts +5 -0
- package/dist/convert-query-params.d.ts.map +1 -1
- package/dist/convert-query-params.js +5 -4
- package/dist/convert-query-params.js.map +1 -1
- package/dist/convert-query-params.mjs +5 -4
- package/dist/convert-query-params.mjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/dist/sanitize/index.d.ts +15 -1
- package/dist/sanitize/index.d.ts.map +1 -1
- package/dist/sanitize/index.js +73 -3
- package/dist/sanitize/index.js.map +1 -1
- package/dist/sanitize/index.mjs +74 -4
- package/dist/sanitize/index.mjs.map +1 -1
- package/dist/sanitize/visitors/index.d.ts +1 -0
- package/dist/sanitize/visitors/index.d.ts.map +1 -1
- package/dist/sanitize/visitors/index.js +2 -0
- package/dist/sanitize/visitors/index.js.map +1 -1
- package/dist/sanitize/visitors/index.mjs +1 -0
- package/dist/sanitize/visitors/index.mjs.map +1 -1
- package/dist/sanitize/visitors/remove-unrecognized-fields.d.ts +4 -0
- package/dist/sanitize/visitors/remove-unrecognized-fields.d.ts.map +1 -0
- package/dist/sanitize/visitors/remove-unrecognized-fields.js +43 -0
- package/dist/sanitize/visitors/remove-unrecognized-fields.js.map +1 -0
- package/dist/sanitize/visitors/remove-unrecognized-fields.mjs +41 -0
- package/dist/sanitize/visitors/remove-unrecognized-fields.mjs.map +1 -0
- package/dist/traverse-entity.d.ts +4 -0
- package/dist/traverse-entity.d.ts.map +1 -1
- package/dist/traverse-entity.js +13 -7
- package/dist/traverse-entity.js.map +1 -1
- package/dist/traverse-entity.mjs +13 -7
- package/dist/traverse-entity.mjs.map +1 -1
- package/dist/validate/index.d.ts +14 -1
- package/dist/validate/index.d.ts.map +1 -1
- package/dist/validate/index.js +83 -7
- package/dist/validate/index.js.map +1 -1
- package/dist/validate/index.mjs +83 -7
- package/dist/validate/index.mjs.map +1 -1
- package/dist/validate/validators.js +0 -1
- package/dist/validate/validators.js.map +1 -1
- package/dist/validate/validators.mjs +0 -1
- package/dist/validate/validators.mjs.map +1 -1
- package/dist/validate/visitors/throw-unrecognized-fields.d.ts.map +1 -1
- package/dist/validate/visitors/throw-unrecognized-fields.js +16 -32
- package/dist/validate/visitors/throw-unrecognized-fields.js.map +1 -1
- package/dist/validate/visitors/throw-unrecognized-fields.mjs +17 -33
- package/dist/validate/visitors/throw-unrecognized-fields.mjs.map +1 -1
- package/dist/zod-schema.d.ts +17 -0
- package/dist/zod-schema.d.ts.map +1 -0
- package/package.json +4 -4
package/dist/sanitize/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var fp = require('lodash/fp');
|
|
4
4
|
var contentTypes = require('../content-types.js');
|
|
5
|
+
var contentApiConstants = require('../content-api-constants.js');
|
|
6
|
+
var contentApiRouteParams = require('../content-api-route-params.js');
|
|
5
7
|
var async = require('../async.js');
|
|
6
8
|
var index = require('./visitors/index.js');
|
|
7
9
|
var sanitizers = require('./sanitizers.js');
|
|
@@ -10,20 +12,24 @@ var queryFilters = require('../traverse/query-filters.js');
|
|
|
10
12
|
var querySort = require('../traverse/query-sort.js');
|
|
11
13
|
var queryPopulate = require('../traverse/query-populate.js');
|
|
12
14
|
require('../traverse/query-fields.js');
|
|
15
|
+
var removeUnrecognizedFields = require('./visitors/remove-unrecognized-fields.js');
|
|
13
16
|
var removeRestrictedFields = require('./visitors/remove-restricted-fields.js');
|
|
14
17
|
var removeRestrictedRelations = require('./visitors/remove-restricted-relations.js');
|
|
15
18
|
|
|
16
19
|
const createAPISanitizers = (opts)=>{
|
|
17
20
|
const { getModel } = opts;
|
|
18
|
-
const sanitizeInput = (data, schema, { auth } = {})=>{
|
|
21
|
+
const sanitizeInput = (data, schema, { auth, strictParams = false, route } = {})=>{
|
|
19
22
|
if (!schema) {
|
|
20
23
|
throw new Error('Missing schema in sanitizeInput');
|
|
21
24
|
}
|
|
22
25
|
if (fp.isArray(data)) {
|
|
23
26
|
return Promise.all(data.map((entry)=>sanitizeInput(entry, schema, {
|
|
24
|
-
auth
|
|
27
|
+
auth,
|
|
28
|
+
strictParams,
|
|
29
|
+
route
|
|
25
30
|
})));
|
|
26
31
|
}
|
|
32
|
+
const allowedExtraRootKeys = contentApiRouteParams.getExtraRootKeysFromRouteBody(route);
|
|
27
33
|
const nonWritableAttributes = contentTypes.getNonWritableAttributes(schema);
|
|
28
34
|
const transforms = [
|
|
29
35
|
// Remove first level ID in inputs
|
|
@@ -35,6 +41,14 @@ const createAPISanitizers = (opts)=>{
|
|
|
35
41
|
getModel
|
|
36
42
|
})
|
|
37
43
|
];
|
|
44
|
+
if (strictParams) {
|
|
45
|
+
// Remove unrecognized fields (allowedExtraRootKeys = registered input param keys)
|
|
46
|
+
transforms.push(traverseEntity(removeUnrecognizedFields, {
|
|
47
|
+
schema,
|
|
48
|
+
getModel,
|
|
49
|
+
allowedExtraRootKeys
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
38
52
|
if (auth) {
|
|
39
53
|
// Remove restricted relations
|
|
40
54
|
transforms.push(traverseEntity(removeRestrictedRelations(auth), {
|
|
@@ -44,6 +58,38 @@ const createAPISanitizers = (opts)=>{
|
|
|
44
58
|
}
|
|
45
59
|
// Apply sanitizers from registry if exists
|
|
46
60
|
opts?.sanitizers?.input?.forEach((sanitizer)=>transforms.push(sanitizer(schema)));
|
|
61
|
+
/**
|
|
62
|
+
* For each extra root key from the route's body schema present in data, run Zod safeParse.
|
|
63
|
+
* If parsing fails, the key is removed from the output.
|
|
64
|
+
*
|
|
65
|
+
* Content-api sends the document payload as body.data; the controller calls sanitizeInput(body.data, ctx),
|
|
66
|
+
* so the input we receive here is the inner payload (keys like "relatedMedia", "name"), not the full body.
|
|
67
|
+
* The route's body schema is z.object({ data: ... }), so its shape includes "data". We skip "data" because
|
|
68
|
+
* the main document payload is already sanitized above by traverseEntity (removeUnrecognizedFields, etc.);
|
|
69
|
+
* relation ops (connect/disconnect/set) are handled there, not by the route's Zod schema. We only run
|
|
70
|
+
* Zod here for truly extra root keys added via addInputParams (e.g. clientMutationId).
|
|
71
|
+
*/ const routeBodySanitizeTransform = async (data)=>{
|
|
72
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) return data;
|
|
73
|
+
const obj = data;
|
|
74
|
+
const bodySchema = route?.request?.body?.['application/json'];
|
|
75
|
+
if (bodySchema && typeof bodySchema === 'object' && 'shape' in bodySchema) {
|
|
76
|
+
const shape = bodySchema.shape;
|
|
77
|
+
for (const key of Object.keys(shape)){
|
|
78
|
+
if (key === 'data' || !(key in obj)) continue;
|
|
79
|
+
const zodSchema = shape[key];
|
|
80
|
+
if (zodSchema && typeof zodSchema.safeParse === 'function') {
|
|
81
|
+
const result = zodSchema.safeParse(obj[key]);
|
|
82
|
+
if (result.success) {
|
|
83
|
+
obj[key] = result.data;
|
|
84
|
+
} else {
|
|
85
|
+
delete obj[key];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return data;
|
|
91
|
+
};
|
|
92
|
+
transforms.push(routeBodySanitizeTransform);
|
|
47
93
|
return async.pipe(...transforms)(data);
|
|
48
94
|
};
|
|
49
95
|
const sanitizeOutput = async (data, schema, { auth } = {})=>{
|
|
@@ -75,7 +121,7 @@ const createAPISanitizers = (opts)=>{
|
|
|
75
121
|
opts?.sanitizers?.output?.forEach((sanitizer)=>transforms.push(sanitizer(schema)));
|
|
76
122
|
return async.pipe(...transforms)(data);
|
|
77
123
|
};
|
|
78
|
-
const sanitizeQuery = async (query, schema, { auth } = {})=>{
|
|
124
|
+
const sanitizeQuery = async (query, schema, { auth, strictParams = false, route } = {})=>{
|
|
79
125
|
if (!schema) {
|
|
80
126
|
throw new Error('Missing schema in sanitizeQuery');
|
|
81
127
|
}
|
|
@@ -105,6 +151,30 @@ const createAPISanitizers = (opts)=>{
|
|
|
105
151
|
populate: await sanitizePopulate(populate, schema)
|
|
106
152
|
});
|
|
107
153
|
}
|
|
154
|
+
const extraQueryKeys = contentApiRouteParams.getExtraQueryKeysFromRoute(route);
|
|
155
|
+
const routeQuerySchema = route?.request?.query;
|
|
156
|
+
if (routeQuerySchema) {
|
|
157
|
+
for (const key of extraQueryKeys){
|
|
158
|
+
if (key in query) {
|
|
159
|
+
const zodSchema = routeQuerySchema[key];
|
|
160
|
+
if (zodSchema && typeof zodSchema.safeParse === 'function') {
|
|
161
|
+
const result = zodSchema.safeParse(query[key]);
|
|
162
|
+
if (result.success) {
|
|
163
|
+
sanitizedQuery[key] = result.data;
|
|
164
|
+
} else {
|
|
165
|
+
delete sanitizedQuery[key];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (strictParams) {
|
|
172
|
+
const allowedKeys = [
|
|
173
|
+
...contentApiConstants.ALLOWED_QUERY_PARAM_KEYS,
|
|
174
|
+
...extraQueryKeys
|
|
175
|
+
];
|
|
176
|
+
return fp.pick(allowedKeys, sanitizedQuery);
|
|
177
|
+
}
|
|
108
178
|
return sanitizedQuery;
|
|
109
179
|
};
|
|
110
180
|
const sanitizeFilters = (filters, schema, { auth } = {})=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/sanitize/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, cloneDeep, omit } from 'lodash/fp';\n\nimport { constants, getNonWritableAttributes } from '../content-types';\nimport { pipe as pipeAsync } from '../async';\n\nimport * as visitors from './visitors';\nimport * as sanitizers from './sanitizers';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\nimport type { Model, Data } from '../types';\n\nexport interface Options {\n auth?: unknown;\n}\n\nexport interface Sanitizer {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface SanitizeFunc {\n (data: unknown, schema: Model, options?: Options): Promise<unknown>;\n}\n\nexport interface APIOptions {\n sanitizers?: Sanitizers;\n getModel: (model: string) => Model;\n}\n\nexport interface Sanitizers {\n input?: Sanitizer[];\n output?: Sanitizer[];\n}\n\nconst createAPISanitizers = (opts: APIOptions) => {\n const { getModel } = opts;\n\n const sanitizeInput: SanitizeFunc = (data: unknown, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeInput');\n }\n if (isArray(data)) {\n return Promise.all(data.map((entry) => sanitizeInput(entry, schema, { auth })));\n }\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n // Remove first level ID in inputs\n omit(constants.ID_ATTRIBUTE),\n omit(constants.DOC_ID_ATTRIBUTE),\n // Remove non-writable attributes\n traverseEntity(visitors.removeRestrictedFields(nonWritableAttributes), { schema, getModel }),\n ];\n\n if (auth) {\n // Remove restricted relations\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.input?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeOutput: SanitizeFunc = async (data, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeOutput');\n }\n if (isArray(data)) {\n const res = new Array(data.length);\n for (let i = 0; i < data.length; i += 1) {\n res[i] = await sanitizeOutput(data[i], schema, { auth });\n }\n return res;\n }\n\n const transforms = [\n (data: Data) => sanitizers.defaultSanitizeOutput({ schema, getModel }, data),\n ];\n\n if (auth) {\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.output?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeQuery = async (\n query: Record<string, unknown>,\n schema: Model,\n { auth }: Options = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeQuery');\n }\n const { filters, sort, fields, populate } = query;\n\n const sanitizedQuery = cloneDeep(query);\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (sort) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n }\n\n if (fields) {\n Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields, schema) });\n }\n\n if (populate) {\n Object.assign(sanitizedQuery, { populate: await sanitizePopulate(populate, schema) });\n }\n\n return sanitizedQuery;\n };\n\n const sanitizeFilters: SanitizeFunc = (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFilters');\n }\n if (isArray(filters)) {\n return Promise.all(filters.map((filter) => sanitizeFilters(filter, schema, { auth })));\n }\n\n const transforms = [sanitizers.defaultSanitizeFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(filters);\n };\n\n const sanitizeSort: SanitizeFunc = (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeSort');\n }\n const transforms = [sanitizers.defaultSanitizeSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(sort);\n };\n\n const sanitizeFields: SanitizeFunc = (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFields');\n }\n const transforms = [sanitizers.defaultSanitizeFields({ schema, getModel })];\n\n return pipeAsync(...transforms)(fields);\n };\n\n const sanitizePopulate: SanitizeFunc = (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [sanitizers.defaultSanitizePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(populate);\n };\n\n return {\n input: sanitizeInput,\n output: sanitizeOutput,\n query: sanitizeQuery,\n filters: sanitizeFilters,\n sort: sanitizeSort,\n fields: sanitizeFields,\n populate: sanitizePopulate,\n };\n};\n\nexport { createAPISanitizers, sanitizers, visitors };\n\nexport type APISanitiers = ReturnType<typeof createAPISanitizers>;\n"],"names":["createAPISanitizers","opts","getModel","sanitizeInput","data","schema","auth","Error","isArray","Promise","all","map","entry","nonWritableAttributes","getNonWritableAttributes","transforms","omit","constants","ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","traverseEntity","visitors","push","sanitizers","input","forEach","sanitizer","pipeAsync","sanitizeOutput","res","Array","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","Object","assign","sanitizeFilters","sanitizeSort","sanitizeFields","sanitizePopulate","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/sanitize/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, cloneDeep, omit, pick } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { constants, getNonWritableAttributes } from '../content-types';\nimport { ALLOWED_QUERY_PARAM_KEYS } from '../content-api-constants';\nimport {\n type RouteLike,\n getExtraQueryKeysFromRoute,\n getExtraRootKeysFromRouteBody,\n} from '../content-api-route-params';\nimport { pipe as pipeAsync } from '../async';\n\nimport * as visitors from './visitors';\nimport * as sanitizers from './sanitizers';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\nimport type { Model, Data } from '../types';\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, removes fields that are not declared in the schema (input) or keeps only allowed query param keys (query).\n * Defaults to false for backward compatibility.\n * TODO: In Strapi 6, strictParams will default to true (and may be removed as an option)\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated/sanitized with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Sanitizer {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface SanitizeFunc {\n (data: unknown, schema: Model, options?: Options): Promise<unknown>;\n}\n\nexport type SanitizeQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport type SanitizeBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport interface APIOptions {\n sanitizers?: Sanitizers;\n getModel: (model: string) => Model;\n}\n\nexport interface Sanitizers {\n input?: Sanitizer[];\n output?: Sanitizer[];\n}\n\nconst createAPISanitizers = (opts: APIOptions) => {\n const { getModel } = opts;\n\n const sanitizeInput: SanitizeFunc = (\n data: unknown,\n schema: Model,\n { auth, strictParams = false, route } = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeInput');\n }\n if (isArray(data)) {\n return Promise.all(\n data.map((entry) => sanitizeInput(entry, schema, { auth, strictParams, route }))\n );\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n // Remove first level ID in inputs\n omit(constants.ID_ATTRIBUTE),\n omit(constants.DOC_ID_ATTRIBUTE),\n // Remove non-writable attributes\n traverseEntity(visitors.removeRestrictedFields(nonWritableAttributes), { schema, getModel }),\n ];\n\n if (strictParams) {\n // Remove unrecognized fields (allowedExtraRootKeys = registered input param keys)\n transforms.push(\n traverseEntity(visitors.removeUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n })\n );\n }\n\n if (auth) {\n // Remove restricted relations\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.input?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n /**\n * For each extra root key from the route's body schema present in data, run Zod safeParse.\n * If parsing fails, the key is removed from the output.\n *\n * Content-api sends the document payload as body.data; the controller calls sanitizeInput(body.data, ctx),\n * so the input we receive here is the inner payload (keys like \"relatedMedia\", \"name\"), not the full body.\n * The route's body schema is z.object({ data: ... }), so its shape includes \"data\". We skip \"data\" because\n * the main document payload is already sanitized above by traverseEntity (removeUnrecognizedFields, etc.);\n * relation ops (connect/disconnect/set) are handled there, not by the route's Zod schema. We only run\n * Zod here for truly extra root keys added via addInputParams (e.g. clientMutationId).\n */\n const routeBodySanitizeTransform = async (data: Data): Promise<Data> => {\n if (!data || typeof data !== 'object' || Array.isArray(data)) return data;\n const obj = data as Record<string, unknown>;\n const bodySchema = route?.request?.body?.['application/json'];\n if (bodySchema && typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in obj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(obj[key]);\n if (result.success) {\n obj[key] = result.data;\n } else {\n delete obj[key];\n }\n }\n }\n }\n return data;\n };\n (transforms as Array<(data: Data) => Data | Promise<Data>>).push(routeBodySanitizeTransform);\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeOutput: SanitizeFunc = async (data, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeOutput');\n }\n if (isArray(data)) {\n const res = new Array(data.length);\n for (let i = 0; i < data.length; i += 1) {\n res[i] = await sanitizeOutput(data[i], schema, { auth });\n }\n return res;\n }\n\n const transforms = [\n (data: Data) => sanitizers.defaultSanitizeOutput({ schema, getModel }, data),\n ];\n\n if (auth) {\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.output?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeQuery = async (\n query: Record<string, unknown>,\n schema: Model,\n { auth, strictParams = false, route }: Options = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeQuery');\n }\n const { filters, sort, fields, populate } = query;\n\n const sanitizedQuery = cloneDeep(query);\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (sort) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n }\n\n if (fields) {\n Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields, schema) });\n }\n\n if (populate) {\n Object.assign(sanitizedQuery, { populate: await sanitizePopulate(populate, schema) });\n }\n\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const routeQuerySchema = route?.request?.query;\n if (routeQuerySchema) {\n for (const key of extraQueryKeys) {\n if (key in query) {\n const zodSchema = routeQuerySchema[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (result.success) {\n sanitizedQuery[key] = result.data;\n } else {\n delete sanitizedQuery[key];\n }\n }\n }\n }\n }\n\n if (strictParams) {\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n return pick(allowedKeys, sanitizedQuery) as Record<string, unknown>;\n }\n\n return sanitizedQuery;\n };\n\n const sanitizeFilters: SanitizeFunc = (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFilters');\n }\n if (isArray(filters)) {\n return Promise.all(filters.map((filter) => sanitizeFilters(filter, schema, { auth })));\n }\n\n const transforms = [sanitizers.defaultSanitizeFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(filters);\n };\n\n const sanitizeSort: SanitizeFunc = (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeSort');\n }\n const transforms = [sanitizers.defaultSanitizeSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(sort);\n };\n\n const sanitizeFields: SanitizeFunc = (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFields');\n }\n const transforms = [sanitizers.defaultSanitizeFields({ schema, getModel })];\n\n return pipeAsync(...transforms)(fields);\n };\n\n const sanitizePopulate: SanitizeFunc = (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [sanitizers.defaultSanitizePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(populate);\n };\n\n return {\n input: sanitizeInput,\n output: sanitizeOutput,\n query: sanitizeQuery,\n filters: sanitizeFilters,\n sort: sanitizeSort,\n fields: sanitizeFields,\n populate: sanitizePopulate,\n };\n};\n\nexport { createAPISanitizers, sanitizers, visitors };\n\nexport type APISanitiers = ReturnType<typeof createAPISanitizers>;\n"],"names":["createAPISanitizers","opts","getModel","sanitizeInput","data","schema","auth","strictParams","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","omit","constants","ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","traverseEntity","visitors","push","sanitizers","input","forEach","sanitizer","routeBodySanitizeTransform","Array","obj","bodySchema","request","body","shape","key","Object","keys","zodSchema","safeParse","result","success","pipeAsync","sanitizeOutput","res","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","assign","sanitizeFilters","sanitizeSort","sanitizeFields","sanitizePopulate","extraQueryKeys","getExtraQueryKeysFromRoute","routeQuerySchema","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","pick","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
|
package/dist/sanitize/index.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { isArray, omit, cloneDeep } from 'lodash/fp';
|
|
1
|
+
import { isArray, omit, cloneDeep, pick } from 'lodash/fp';
|
|
2
2
|
import { getNonWritableAttributes, constants } from '../content-types.mjs';
|
|
3
|
+
import { ALLOWED_QUERY_PARAM_KEYS } from '../content-api-constants.mjs';
|
|
4
|
+
import { getExtraRootKeysFromRouteBody, getExtraQueryKeysFromRoute } from '../content-api-route-params.mjs';
|
|
3
5
|
import { pipe } from '../async.mjs';
|
|
4
6
|
import * as index from './visitors/index.mjs';
|
|
5
7
|
export { index as visitors };
|
|
@@ -11,20 +13,24 @@ import traverseQueryFilters from '../traverse/query-filters.mjs';
|
|
|
11
13
|
import traverseQuerySort from '../traverse/query-sort.mjs';
|
|
12
14
|
import traverseQueryPopulate from '../traverse/query-populate.mjs';
|
|
13
15
|
import '../traverse/query-fields.mjs';
|
|
16
|
+
import removeUnrecognizedFields from './visitors/remove-unrecognized-fields.mjs';
|
|
14
17
|
import removeRestrictedFields from './visitors/remove-restricted-fields.mjs';
|
|
15
18
|
import removeRestrictedRelations from './visitors/remove-restricted-relations.mjs';
|
|
16
19
|
|
|
17
20
|
const createAPISanitizers = (opts)=>{
|
|
18
21
|
const { getModel } = opts;
|
|
19
|
-
const sanitizeInput = (data, schema, { auth } = {})=>{
|
|
22
|
+
const sanitizeInput = (data, schema, { auth, strictParams = false, route } = {})=>{
|
|
20
23
|
if (!schema) {
|
|
21
24
|
throw new Error('Missing schema in sanitizeInput');
|
|
22
25
|
}
|
|
23
26
|
if (isArray(data)) {
|
|
24
27
|
return Promise.all(data.map((entry)=>sanitizeInput(entry, schema, {
|
|
25
|
-
auth
|
|
28
|
+
auth,
|
|
29
|
+
strictParams,
|
|
30
|
+
route
|
|
26
31
|
})));
|
|
27
32
|
}
|
|
33
|
+
const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);
|
|
28
34
|
const nonWritableAttributes = getNonWritableAttributes(schema);
|
|
29
35
|
const transforms = [
|
|
30
36
|
// Remove first level ID in inputs
|
|
@@ -36,6 +42,14 @@ const createAPISanitizers = (opts)=>{
|
|
|
36
42
|
getModel
|
|
37
43
|
})
|
|
38
44
|
];
|
|
45
|
+
if (strictParams) {
|
|
46
|
+
// Remove unrecognized fields (allowedExtraRootKeys = registered input param keys)
|
|
47
|
+
transforms.push(traverseEntity(removeUnrecognizedFields, {
|
|
48
|
+
schema,
|
|
49
|
+
getModel,
|
|
50
|
+
allowedExtraRootKeys
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
39
53
|
if (auth) {
|
|
40
54
|
// Remove restricted relations
|
|
41
55
|
transforms.push(traverseEntity(removeRestrictedRelations(auth), {
|
|
@@ -45,6 +59,38 @@ const createAPISanitizers = (opts)=>{
|
|
|
45
59
|
}
|
|
46
60
|
// Apply sanitizers from registry if exists
|
|
47
61
|
opts?.sanitizers?.input?.forEach((sanitizer)=>transforms.push(sanitizer(schema)));
|
|
62
|
+
/**
|
|
63
|
+
* For each extra root key from the route's body schema present in data, run Zod safeParse.
|
|
64
|
+
* If parsing fails, the key is removed from the output.
|
|
65
|
+
*
|
|
66
|
+
* Content-api sends the document payload as body.data; the controller calls sanitizeInput(body.data, ctx),
|
|
67
|
+
* so the input we receive here is the inner payload (keys like "relatedMedia", "name"), not the full body.
|
|
68
|
+
* The route's body schema is z.object({ data: ... }), so its shape includes "data". We skip "data" because
|
|
69
|
+
* the main document payload is already sanitized above by traverseEntity (removeUnrecognizedFields, etc.);
|
|
70
|
+
* relation ops (connect/disconnect/set) are handled there, not by the route's Zod schema. We only run
|
|
71
|
+
* Zod here for truly extra root keys added via addInputParams (e.g. clientMutationId).
|
|
72
|
+
*/ const routeBodySanitizeTransform = async (data)=>{
|
|
73
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) return data;
|
|
74
|
+
const obj = data;
|
|
75
|
+
const bodySchema = route?.request?.body?.['application/json'];
|
|
76
|
+
if (bodySchema && typeof bodySchema === 'object' && 'shape' in bodySchema) {
|
|
77
|
+
const shape = bodySchema.shape;
|
|
78
|
+
for (const key of Object.keys(shape)){
|
|
79
|
+
if (key === 'data' || !(key in obj)) continue;
|
|
80
|
+
const zodSchema = shape[key];
|
|
81
|
+
if (zodSchema && typeof zodSchema.safeParse === 'function') {
|
|
82
|
+
const result = zodSchema.safeParse(obj[key]);
|
|
83
|
+
if (result.success) {
|
|
84
|
+
obj[key] = result.data;
|
|
85
|
+
} else {
|
|
86
|
+
delete obj[key];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return data;
|
|
92
|
+
};
|
|
93
|
+
transforms.push(routeBodySanitizeTransform);
|
|
48
94
|
return pipe(...transforms)(data);
|
|
49
95
|
};
|
|
50
96
|
const sanitizeOutput = async (data, schema, { auth } = {})=>{
|
|
@@ -76,7 +122,7 @@ const createAPISanitizers = (opts)=>{
|
|
|
76
122
|
opts?.sanitizers?.output?.forEach((sanitizer)=>transforms.push(sanitizer(schema)));
|
|
77
123
|
return pipe(...transforms)(data);
|
|
78
124
|
};
|
|
79
|
-
const sanitizeQuery = async (query, schema, { auth } = {})=>{
|
|
125
|
+
const sanitizeQuery = async (query, schema, { auth, strictParams = false, route } = {})=>{
|
|
80
126
|
if (!schema) {
|
|
81
127
|
throw new Error('Missing schema in sanitizeQuery');
|
|
82
128
|
}
|
|
@@ -106,6 +152,30 @@ const createAPISanitizers = (opts)=>{
|
|
|
106
152
|
populate: await sanitizePopulate(populate, schema)
|
|
107
153
|
});
|
|
108
154
|
}
|
|
155
|
+
const extraQueryKeys = getExtraQueryKeysFromRoute(route);
|
|
156
|
+
const routeQuerySchema = route?.request?.query;
|
|
157
|
+
if (routeQuerySchema) {
|
|
158
|
+
for (const key of extraQueryKeys){
|
|
159
|
+
if (key in query) {
|
|
160
|
+
const zodSchema = routeQuerySchema[key];
|
|
161
|
+
if (zodSchema && typeof zodSchema.safeParse === 'function') {
|
|
162
|
+
const result = zodSchema.safeParse(query[key]);
|
|
163
|
+
if (result.success) {
|
|
164
|
+
sanitizedQuery[key] = result.data;
|
|
165
|
+
} else {
|
|
166
|
+
delete sanitizedQuery[key];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (strictParams) {
|
|
173
|
+
const allowedKeys = [
|
|
174
|
+
...ALLOWED_QUERY_PARAM_KEYS,
|
|
175
|
+
...extraQueryKeys
|
|
176
|
+
];
|
|
177
|
+
return pick(allowedKeys, sanitizedQuery);
|
|
178
|
+
}
|
|
109
179
|
return sanitizedQuery;
|
|
110
180
|
};
|
|
111
181
|
const sanitizeFilters = (filters, schema, { auth } = {})=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../src/sanitize/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, cloneDeep, omit } from 'lodash/fp';\n\nimport { constants, getNonWritableAttributes } from '../content-types';\nimport { pipe as pipeAsync } from '../async';\n\nimport * as visitors from './visitors';\nimport * as sanitizers from './sanitizers';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\nimport type { Model, Data } from '../types';\n\nexport interface Options {\n auth?: unknown;\n}\n\nexport interface Sanitizer {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface SanitizeFunc {\n (data: unknown, schema: Model, options?: Options): Promise<unknown>;\n}\n\nexport interface APIOptions {\n sanitizers?: Sanitizers;\n getModel: (model: string) => Model;\n}\n\nexport interface Sanitizers {\n input?: Sanitizer[];\n output?: Sanitizer[];\n}\n\nconst createAPISanitizers = (opts: APIOptions) => {\n const { getModel } = opts;\n\n const sanitizeInput: SanitizeFunc = (data: unknown, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeInput');\n }\n if (isArray(data)) {\n return Promise.all(data.map((entry) => sanitizeInput(entry, schema, { auth })));\n }\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n // Remove first level ID in inputs\n omit(constants.ID_ATTRIBUTE),\n omit(constants.DOC_ID_ATTRIBUTE),\n // Remove non-writable attributes\n traverseEntity(visitors.removeRestrictedFields(nonWritableAttributes), { schema, getModel }),\n ];\n\n if (auth) {\n // Remove restricted relations\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.input?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeOutput: SanitizeFunc = async (data, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeOutput');\n }\n if (isArray(data)) {\n const res = new Array(data.length);\n for (let i = 0; i < data.length; i += 1) {\n res[i] = await sanitizeOutput(data[i], schema, { auth });\n }\n return res;\n }\n\n const transforms = [\n (data: Data) => sanitizers.defaultSanitizeOutput({ schema, getModel }, data),\n ];\n\n if (auth) {\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.output?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeQuery = async (\n query: Record<string, unknown>,\n schema: Model,\n { auth }: Options = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeQuery');\n }\n const { filters, sort, fields, populate } = query;\n\n const sanitizedQuery = cloneDeep(query);\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (sort) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n }\n\n if (fields) {\n Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields, schema) });\n }\n\n if (populate) {\n Object.assign(sanitizedQuery, { populate: await sanitizePopulate(populate, schema) });\n }\n\n return sanitizedQuery;\n };\n\n const sanitizeFilters: SanitizeFunc = (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFilters');\n }\n if (isArray(filters)) {\n return Promise.all(filters.map((filter) => sanitizeFilters(filter, schema, { auth })));\n }\n\n const transforms = [sanitizers.defaultSanitizeFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(filters);\n };\n\n const sanitizeSort: SanitizeFunc = (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeSort');\n }\n const transforms = [sanitizers.defaultSanitizeSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(sort);\n };\n\n const sanitizeFields: SanitizeFunc = (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFields');\n }\n const transforms = [sanitizers.defaultSanitizeFields({ schema, getModel })];\n\n return pipeAsync(...transforms)(fields);\n };\n\n const sanitizePopulate: SanitizeFunc = (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [sanitizers.defaultSanitizePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(populate);\n };\n\n return {\n input: sanitizeInput,\n output: sanitizeOutput,\n query: sanitizeQuery,\n filters: sanitizeFilters,\n sort: sanitizeSort,\n fields: sanitizeFields,\n populate: sanitizePopulate,\n };\n};\n\nexport { createAPISanitizers, sanitizers, visitors };\n\nexport type APISanitiers = ReturnType<typeof createAPISanitizers>;\n"],"names":["createAPISanitizers","opts","getModel","sanitizeInput","data","schema","auth","Error","isArray","Promise","all","map","entry","nonWritableAttributes","getNonWritableAttributes","transforms","omit","constants","ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","traverseEntity","visitors","push","sanitizers","input","forEach","sanitizer","pipeAsync","sanitizeOutput","res","Array","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","Object","assign","sanitizeFilters","sanitizeSort","sanitizeFields","sanitizePopulate","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":";;;;;;;;;;;;;;;;AAkCA,MAAMA,sBAAsB,CAACC,IAAAA,GAAAA;IAC3B,MAAM,EAAEC,QAAQ,EAAE,GAAGD,IAAAA;IAErB,MAAME,aAAAA,GAA8B,CAACC,IAAeC,EAAAA,MAAAA,EAAe,EAAEC,IAAI,EAAE,GAAG,EAAE,GAAA;AAC9E,QAAA,IAAI,CAACD,MAAQ,EAAA;AACX,YAAA,MAAM,IAAIE,KAAM,CAAA,iCAAA,CAAA;AAClB;AACA,QAAA,IAAIC,QAAQJ,IAAO,CAAA,EAAA;YACjB,OAAOK,OAAAA,CAAQC,GAAG,CAACN,IAAKO,CAAAA,GAAG,CAAC,CAACC,KAAAA,GAAUT,aAAcS,CAAAA,KAAAA,EAAOP,MAAQ,EAAA;AAAEC,oBAAAA;AAAK,iBAAA,CAAA,CAAA,CAAA;AAC7E;AAEA,QAAA,MAAMO,wBAAwBC,wBAAyBT,CAAAA,MAAAA,CAAAA;AAEvD,QAAA,MAAMU,UAAa,GAAA;;AAEjBC,YAAAA,IAAAA,CAAKC,UAAUC,YAAY,CAAA;AAC3BF,YAAAA,IAAAA,CAAKC,UAAUE,gBAAgB,CAAA;;YAE/BC,cAAeC,CAAAA,sBAA+B,CAACR,qBAAwB,CAAA,EAAA;AAAER,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA;AAC3F,SAAA;AAED,QAAA,IAAII,IAAM,EAAA;;AAERS,YAAAA,UAAAA,CAAWO,IAAI,CACbF,cAAAA,CAAeC,yBAAkC,CAACf,IAAO,CAAA,EAAA;AAAED,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA,CAAA,CAAA;AAEhF;;QAGAD,IAAMsB,EAAAA,UAAAA,EAAYC,OAAOC,OAAQ,CAAA,CAACC,YAAyBX,UAAWO,CAAAA,IAAI,CAACI,SAAUrB,CAAAA,MAAAA,CAAAA,CAAAA,CAAAA;AAErF,QAAA,OAAOsB,QAAaZ,UAAYX,CAAAA,CAAAA,IAAAA,CAAAA;AAClC,KAAA;IAEA,MAAMwB,cAAAA,GAA+B,OAAOxB,IAAMC,EAAAA,MAAAA,EAAe,EAAEC,IAAI,EAAE,GAAG,EAAE,GAAA;AAC5E,QAAA,IAAI,CAACD,MAAQ,EAAA;AACX,YAAA,MAAM,IAAIE,KAAM,CAAA,kCAAA,CAAA;AAClB;AACA,QAAA,IAAIC,QAAQJ,IAAO,CAAA,EAAA;AACjB,YAAA,MAAMyB,GAAM,GAAA,IAAIC,KAAM1B,CAAAA,IAAAA,CAAK2B,MAAM,CAAA;YACjC,IAAK,IAAIC,IAAI,CAAGA,EAAAA,CAAAA,GAAI5B,KAAK2B,MAAM,EAAEC,KAAK,CAAG,CAAA;gBACvCH,GAAG,CAACG,EAAE,GAAG,MAAMJ,eAAexB,IAAI,CAAC4B,CAAE,CAAA,EAAE3B,MAAQ,EAAA;AAAEC,oBAAAA;AAAK,iBAAA,CAAA;AACxD;YACA,OAAOuB,GAAAA;AACT;AAEA,QAAA,MAAMd,UAAa,GAAA;YACjB,CAACX,IAAAA,GAAemB,qBAAgC,CAAC;AAAElB,oBAAAA,MAAAA;AAAQH,oBAAAA;iBAAYE,EAAAA,IAAAA;AACxE,SAAA;AAED,QAAA,IAAIE,IAAM,EAAA;AACRS,YAAAA,UAAAA,CAAWO,IAAI,CACbF,cAAAA,CAAeC,yBAAkC,CAACf,IAAO,CAAA,EAAA;AAAED,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA,CAAA,CAAA;AAEhF;;QAGAD,IAAMsB,EAAAA,UAAAA,EAAYU,QAAQR,OAAQ,CAAA,CAACC,YAAyBX,UAAWO,CAAAA,IAAI,CAACI,SAAUrB,CAAAA,MAAAA,CAAAA,CAAAA,CAAAA;AAEtF,QAAA,OAAOsB,QAAaZ,UAAYX,CAAAA,CAAAA,IAAAA,CAAAA;AAClC,KAAA;IAEA,MAAM8B,aAAAA,GAAgB,OACpBC,KACA9B,EAAAA,MAAAA,EACA,EAAEC,IAAI,EAAW,GAAG,EAAE,GAAA;AAEtB,QAAA,IAAI,CAACD,MAAQ,EAAA;AACX,YAAA,MAAM,IAAIE,KAAM,CAAA,iCAAA,CAAA;AAClB;QACA,MAAM,EAAE6B,OAAO,EAAEC,IAAI,EAAEC,MAAM,EAAEC,QAAQ,EAAE,GAAGJ,KAAAA;AAE5C,QAAA,MAAMK,iBAAiBC,SAAUN,CAAAA,KAAAA,CAAAA;AAEjC,QAAA,IAAIC,OAAS,EAAA;YACXM,MAAOC,CAAAA,MAAM,CAACH,cAAgB,EAAA;gBAAEJ,OAAS,EAAA,MAAMQ,eAAgBR,CAAAA,OAAAA,EAAS/B,MAAQ,EAAA;AAAEC,oBAAAA;AAAK,iBAAA;AAAG,aAAA,CAAA;AAC5F;AAEA,QAAA,IAAI+B,IAAM,EAAA;YACRK,MAAOC,CAAAA,MAAM,CAACH,cAAgB,EAAA;gBAAEH,IAAM,EAAA,MAAMQ,YAAaR,CAAAA,IAAAA,EAAMhC,MAAQ,EAAA;AAAEC,oBAAAA;AAAK,iBAAA;AAAG,aAAA,CAAA;AACnF;AAEA,QAAA,IAAIgC,MAAQ,EAAA;YACVI,MAAOC,CAAAA,MAAM,CAACH,cAAgB,EAAA;gBAAEF,MAAQ,EAAA,MAAMQ,eAAeR,MAAQjC,EAAAA,MAAAA;AAAQ,aAAA,CAAA;AAC/E;AAEA,QAAA,IAAIkC,QAAU,EAAA;YACZG,MAAOC,CAAAA,MAAM,CAACH,cAAgB,EAAA;gBAAED,QAAU,EAAA,MAAMQ,iBAAiBR,QAAUlC,EAAAA,MAAAA;AAAQ,aAAA,CAAA;AACrF;QAEA,OAAOmC,cAAAA;AACT,KAAA;IAEA,MAAMI,eAAAA,GAAgC,CAACR,OAAS/B,EAAAA,MAAAA,EAAe,EAAEC,IAAI,EAAE,GAAG,EAAE,GAAA;AAC1E,QAAA,IAAI,CAACD,MAAQ,EAAA;AACX,YAAA,MAAM,IAAIE,KAAM,CAAA,mCAAA,CAAA;AAClB;AACA,QAAA,IAAIC,QAAQ4B,OAAU,CAAA,EAAA;YACpB,OAAO3B,OAAAA,CAAQC,GAAG,CAAC0B,OAAQzB,CAAAA,GAAG,CAAC,CAACqC,MAAAA,GAAWJ,eAAgBI,CAAAA,MAAAA,EAAQ3C,MAAQ,EAAA;AAAEC,oBAAAA;AAAK,iBAAA,CAAA,CAAA,CAAA;AACpF;AAEA,QAAA,MAAMS,UAAa,GAAA;AAACQ,YAAAA,sBAAiC,CAAC;AAAElB,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA;AAAG,SAAA;AAE5E,QAAA,IAAII,IAAM,EAAA;AACRS,YAAAA,UAAAA,CAAWO,IAAI,CACb2B,oBAAAA,CAAqB5B,yBAAkC,CAACf,IAAO,CAAA,EAAA;AAAED,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA,CAAA,CAAA;AAEtF;AAEA,QAAA,OAAOyB,QAAaZ,UAAYqB,CAAAA,CAAAA,OAAAA,CAAAA;AAClC,KAAA;IAEA,MAAMS,YAAAA,GAA6B,CAACR,IAAMhC,EAAAA,MAAAA,EAAe,EAAEC,IAAI,EAAE,GAAG,EAAE,GAAA;AACpE,QAAA,IAAI,CAACD,MAAQ,EAAA;AACX,YAAA,MAAM,IAAIE,KAAM,CAAA,gCAAA,CAAA;AAClB;AACA,QAAA,MAAMQ,UAAa,GAAA;AAACQ,YAAAA,mBAA8B,CAAC;AAAElB,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA;AAAG,SAAA;AAEzE,QAAA,IAAII,IAAM,EAAA;AACRS,YAAAA,UAAAA,CAAWO,IAAI,CACb4B,iBAAAA,CAAkB7B,yBAAkC,CAACf,IAAO,CAAA,EAAA;AAAED,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA,CAAA,CAAA;AAEnF;AAEA,QAAA,OAAOyB,QAAaZ,UAAYsB,CAAAA,CAAAA,IAAAA,CAAAA;AAClC,KAAA;IAEA,MAAMS,cAAAA,GAA+B,CAACR,MAAQjC,EAAAA,MAAAA,GAAAA;AAC5C,QAAA,IAAI,CAACA,MAAQ,EAAA;AACX,YAAA,MAAM,IAAIE,KAAM,CAAA,kCAAA,CAAA;AAClB;AACA,QAAA,MAAMQ,UAAa,GAAA;AAACQ,YAAAA,qBAAgC,CAAC;AAAElB,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA;AAAG,SAAA;AAE3E,QAAA,OAAOyB,QAAaZ,UAAYuB,CAAAA,CAAAA,MAAAA,CAAAA;AAClC,KAAA;IAEA,MAAMS,gBAAAA,GAAiC,CAACR,QAAUlC,EAAAA,MAAAA,EAAe,EAAEC,IAAI,EAAE,GAAG,EAAE,GAAA;AAC5E,QAAA,IAAI,CAACD,MAAQ,EAAA;AACX,YAAA,MAAM,IAAIE,KAAM,CAAA,oCAAA,CAAA;AAClB;AACA,QAAA,MAAMQ,UAAa,GAAA;AAACQ,YAAAA,uBAAkC,CAAC;AAAElB,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA;AAAG,SAAA;AAE7E,QAAA,IAAII,IAAM,EAAA;AACRS,YAAAA,UAAAA,CAAWO,IAAI,CACb6B,qBAAAA,CAAsB9B,yBAAkC,CAACf,IAAO,CAAA,EAAA;AAAED,gBAAAA,MAAAA;AAAQH,gBAAAA;AAAS,aAAA,CAAA,CAAA;AAEvF;AAEA,QAAA,OAAOyB,QAAaZ,UAAYwB,CAAAA,CAAAA,QAAAA,CAAAA;AAClC,KAAA;IAEA,OAAO;QACLf,KAAOrB,EAAAA,aAAAA;QACP8B,MAAQL,EAAAA,cAAAA;QACRO,KAAOD,EAAAA,aAAAA;QACPE,OAASQ,EAAAA,eAAAA;QACTP,IAAMQ,EAAAA,YAAAA;QACNP,MAAQQ,EAAAA,cAAAA;QACRP,QAAUQ,EAAAA;AACZ,KAAA;AACF;;;;"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../src/sanitize/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, cloneDeep, omit, pick } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { constants, getNonWritableAttributes } from '../content-types';\nimport { ALLOWED_QUERY_PARAM_KEYS } from '../content-api-constants';\nimport {\n type RouteLike,\n getExtraQueryKeysFromRoute,\n getExtraRootKeysFromRouteBody,\n} from '../content-api-route-params';\nimport { pipe as pipeAsync } from '../async';\n\nimport * as visitors from './visitors';\nimport * as sanitizers from './sanitizers';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\nimport type { Model, Data } from '../types';\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, removes fields that are not declared in the schema (input) or keeps only allowed query param keys (query).\n * Defaults to false for backward compatibility.\n * TODO: In Strapi 6, strictParams will default to true (and may be removed as an option)\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated/sanitized with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Sanitizer {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface SanitizeFunc {\n (data: unknown, schema: Model, options?: Options): Promise<unknown>;\n}\n\nexport type SanitizeQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport type SanitizeBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<unknown>;\n\nexport interface APIOptions {\n sanitizers?: Sanitizers;\n getModel: (model: string) => Model;\n}\n\nexport interface Sanitizers {\n input?: Sanitizer[];\n output?: Sanitizer[];\n}\n\nconst createAPISanitizers = (opts: APIOptions) => {\n const { getModel } = opts;\n\n const sanitizeInput: SanitizeFunc = (\n data: unknown,\n schema: Model,\n { auth, strictParams = false, route } = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeInput');\n }\n if (isArray(data)) {\n return Promise.all(\n data.map((entry) => sanitizeInput(entry, schema, { auth, strictParams, route }))\n );\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n // Remove first level ID in inputs\n omit(constants.ID_ATTRIBUTE),\n omit(constants.DOC_ID_ATTRIBUTE),\n // Remove non-writable attributes\n traverseEntity(visitors.removeRestrictedFields(nonWritableAttributes), { schema, getModel }),\n ];\n\n if (strictParams) {\n // Remove unrecognized fields (allowedExtraRootKeys = registered input param keys)\n transforms.push(\n traverseEntity(visitors.removeUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n })\n );\n }\n\n if (auth) {\n // Remove restricted relations\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.input?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n /**\n * For each extra root key from the route's body schema present in data, run Zod safeParse.\n * If parsing fails, the key is removed from the output.\n *\n * Content-api sends the document payload as body.data; the controller calls sanitizeInput(body.data, ctx),\n * so the input we receive here is the inner payload (keys like \"relatedMedia\", \"name\"), not the full body.\n * The route's body schema is z.object({ data: ... }), so its shape includes \"data\". We skip \"data\" because\n * the main document payload is already sanitized above by traverseEntity (removeUnrecognizedFields, etc.);\n * relation ops (connect/disconnect/set) are handled there, not by the route's Zod schema. We only run\n * Zod here for truly extra root keys added via addInputParams (e.g. clientMutationId).\n */\n const routeBodySanitizeTransform = async (data: Data): Promise<Data> => {\n if (!data || typeof data !== 'object' || Array.isArray(data)) return data;\n const obj = data as Record<string, unknown>;\n const bodySchema = route?.request?.body?.['application/json'];\n if (bodySchema && typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in obj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(obj[key]);\n if (result.success) {\n obj[key] = result.data;\n } else {\n delete obj[key];\n }\n }\n }\n }\n return data;\n };\n (transforms as Array<(data: Data) => Data | Promise<Data>>).push(routeBodySanitizeTransform);\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeOutput: SanitizeFunc = async (data, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeOutput');\n }\n if (isArray(data)) {\n const res = new Array(data.length);\n for (let i = 0; i < data.length; i += 1) {\n res[i] = await sanitizeOutput(data[i], schema, { auth });\n }\n return res;\n }\n\n const transforms = [\n (data: Data) => sanitizers.defaultSanitizeOutput({ schema, getModel }, data),\n ];\n\n if (auth) {\n transforms.push(\n traverseEntity(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n // Apply sanitizers from registry if exists\n opts?.sanitizers?.output?.forEach((sanitizer: Sanitizer) => transforms.push(sanitizer(schema)));\n\n return pipeAsync(...transforms)(data as Data);\n };\n\n const sanitizeQuery = async (\n query: Record<string, unknown>,\n schema: Model,\n { auth, strictParams = false, route }: Options = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeQuery');\n }\n const { filters, sort, fields, populate } = query;\n\n const sanitizedQuery = cloneDeep(query);\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (sort) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n }\n\n if (fields) {\n Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields, schema) });\n }\n\n if (populate) {\n Object.assign(sanitizedQuery, { populate: await sanitizePopulate(populate, schema) });\n }\n\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const routeQuerySchema = route?.request?.query;\n if (routeQuerySchema) {\n for (const key of extraQueryKeys) {\n if (key in query) {\n const zodSchema = routeQuerySchema[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).safeParse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (result.success) {\n sanitizedQuery[key] = result.data;\n } else {\n delete sanitizedQuery[key];\n }\n }\n }\n }\n }\n\n if (strictParams) {\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n return pick(allowedKeys, sanitizedQuery) as Record<string, unknown>;\n }\n\n return sanitizedQuery;\n };\n\n const sanitizeFilters: SanitizeFunc = (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFilters');\n }\n if (isArray(filters)) {\n return Promise.all(filters.map((filter) => sanitizeFilters(filter, schema, { auth })));\n }\n\n const transforms = [sanitizers.defaultSanitizeFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(filters);\n };\n\n const sanitizeSort: SanitizeFunc = (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeSort');\n }\n const transforms = [sanitizers.defaultSanitizeSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(sort);\n };\n\n const sanitizeFields: SanitizeFunc = (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizeFields');\n }\n const transforms = [sanitizers.defaultSanitizeFields({ schema, getModel })];\n\n return pipeAsync(...transforms)(fields);\n };\n\n const sanitizePopulate: SanitizeFunc = (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [sanitizers.defaultSanitizePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.removeRestrictedRelations(auth), { schema, getModel })\n );\n }\n\n return pipeAsync(...transforms)(populate);\n };\n\n return {\n input: sanitizeInput,\n output: sanitizeOutput,\n query: sanitizeQuery,\n filters: sanitizeFilters,\n sort: sanitizeSort,\n fields: sanitizeFields,\n populate: sanitizePopulate,\n };\n};\n\nexport { createAPISanitizers, sanitizers, visitors };\n\nexport type APISanitiers = ReturnType<typeof createAPISanitizers>;\n"],"names":["createAPISanitizers","opts","getModel","sanitizeInput","data","schema","auth","strictParams","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","omit","constants","ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","traverseEntity","visitors","push","sanitizers","input","forEach","sanitizer","routeBodySanitizeTransform","Array","obj","bodySchema","request","body","shape","key","Object","keys","zodSchema","safeParse","result","success","pipeAsync","sanitizeOutput","res","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","assign","sanitizeFilters","sanitizeSort","sanitizeFields","sanitizePopulate","extraQueryKeys","getExtraQueryKeysFromRoute","routeQuerySchema","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","pick","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
|
|
@@ -5,5 +5,6 @@ export { default as removeMorphToRelations } from './remove-morph-to-relations';
|
|
|
5
5
|
export { default as removeDynamicZones } from './remove-dynamic-zones';
|
|
6
6
|
export { default as removeDisallowedFields } from './remove-disallowed-fields';
|
|
7
7
|
export { default as removeRestrictedFields } from './remove-restricted-fields';
|
|
8
|
+
export { default as removeUnrecognizedFields } from './remove-unrecognized-fields';
|
|
8
9
|
export { default as expandWildcardPopulate } from './expand-wildcard-populate';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sanitize/visitors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AACrF,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sanitize/visitors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AACrF,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -7,6 +7,7 @@ var removeMorphToRelations = require('./remove-morph-to-relations.js');
|
|
|
7
7
|
var removeDynamicZones = require('./remove-dynamic-zones.js');
|
|
8
8
|
var removeDisallowedFields = require('./remove-disallowed-fields.js');
|
|
9
9
|
var removeRestrictedFields = require('./remove-restricted-fields.js');
|
|
10
|
+
var removeUnrecognizedFields = require('./remove-unrecognized-fields.js');
|
|
10
11
|
var expandWildcardPopulate = require('./expand-wildcard-populate.js');
|
|
11
12
|
|
|
12
13
|
|
|
@@ -18,5 +19,6 @@ exports.removeMorphToRelations = removeMorphToRelations;
|
|
|
18
19
|
exports.removeDynamicZones = removeDynamicZones;
|
|
19
20
|
exports.removeDisallowedFields = removeDisallowedFields;
|
|
20
21
|
exports.removeRestrictedFields = removeRestrictedFields;
|
|
22
|
+
exports.removeUnrecognizedFields = removeUnrecognizedFields;
|
|
21
23
|
exports.expandWildcardPopulate = expandWildcardPopulate;
|
|
22
24
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -5,5 +5,6 @@ export { default as removeMorphToRelations } from './remove-morph-to-relations.m
|
|
|
5
5
|
export { default as removeDynamicZones } from './remove-dynamic-zones.mjs';
|
|
6
6
|
export { default as removeDisallowedFields } from './remove-disallowed-fields.mjs';
|
|
7
7
|
export { default as removeRestrictedFields } from './remove-restricted-fields.mjs';
|
|
8
|
+
export { default as removeUnrecognizedFields } from './remove-unrecognized-fields.mjs';
|
|
8
9
|
export { default as expandWildcardPopulate } from './expand-wildcard-populate.mjs';
|
|
9
10
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove-unrecognized-fields.d.ts","sourceRoot":"","sources":["../../../src/sanitize/visitors/remove-unrecognized-fields.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,QAAA,MAAM,wBAAwB,EAAE,OAuD/B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var contentTypes = require('../../content-types.js');
|
|
4
|
+
|
|
5
|
+
const removeUnrecognizedFields = ({ key, attribute, path, schema, parent, allowedExtraRootKeys }, { remove })=>{
|
|
6
|
+
// We only look at properties that are not attributes
|
|
7
|
+
if (attribute) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
// At root level (path.attribute === null), only accept id-like fields
|
|
11
|
+
if (path.attribute === null) {
|
|
12
|
+
if (contentTypes.ID_FIELDS.includes(key)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (allowedExtraRootKeys?.includes(key)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
remove(key);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// allow special morphTo keys
|
|
22
|
+
if (contentTypes.isMorphToRelationalAttribute(parent?.attribute) && contentTypes.MORPH_TO_KEYS.includes(key)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// allow special dz keys
|
|
26
|
+
if (contentTypes.isComponentSchema(schema) && contentTypes.isDynamicZoneAttribute(parent?.attribute) && contentTypes.DYNAMIC_ZONE_KEYS.includes(key)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// allow relation operation keys (connect, disconnect, set, options) for relations and media
|
|
30
|
+
if ((contentTypes.isRelationalAttribute(parent?.attribute) || contentTypes.isMediaAttribute(parent?.attribute)) && contentTypes.RELATION_OPERATION_KEYS.includes(key)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// allow id fields for relations, media, and components
|
|
34
|
+
const canUseID = contentTypes.isRelationalAttribute(parent?.attribute) || contentTypes.isMediaAttribute(parent?.attribute) || contentTypes.isComponentAttribute(parent?.attribute);
|
|
35
|
+
if (canUseID && contentTypes.ID_FIELDS.includes(key)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// if we couldn't find any reason for it to be here, remove it
|
|
39
|
+
remove(key);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
module.exports = removeUnrecognizedFields;
|
|
43
|
+
//# sourceMappingURL=remove-unrecognized-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove-unrecognized-fields.js","sources":["../../../src/sanitize/visitors/remove-unrecognized-fields.ts"],"sourcesContent":["import {\n isDynamicZoneAttribute,\n isMorphToRelationalAttribute,\n isRelationalAttribute,\n isComponentSchema,\n isMediaAttribute,\n isComponentAttribute,\n DYNAMIC_ZONE_KEYS,\n ID_FIELDS,\n MORPH_TO_KEYS,\n RELATION_OPERATION_KEYS,\n} from '../../content-types';\nimport type { Visitor } from '../../traverse-entity';\n\nconst removeUnrecognizedFields: Visitor = (\n { key, attribute, path, schema, parent, allowedExtraRootKeys },\n { remove }\n) => {\n // We only look at properties that are not attributes\n if (attribute) {\n return;\n }\n\n // At root level (path.attribute === null), only accept id-like fields\n if (path.attribute === null) {\n if (ID_FIELDS.includes(key)) {\n return;\n }\n if (allowedExtraRootKeys?.includes(key)) {\n return;\n }\n\n remove(key);\n return;\n }\n\n // allow special morphTo keys\n if (isMorphToRelationalAttribute(parent?.attribute) && MORPH_TO_KEYS.includes(key)) {\n return;\n }\n\n // allow special dz keys\n if (\n isComponentSchema(schema) &&\n isDynamicZoneAttribute(parent?.attribute) &&\n DYNAMIC_ZONE_KEYS.includes(key)\n ) {\n return;\n }\n\n // allow relation operation keys (connect, disconnect, set, options) for relations and media\n if (\n (isRelationalAttribute(parent?.attribute) || isMediaAttribute(parent?.attribute)) &&\n RELATION_OPERATION_KEYS.includes(key)\n ) {\n return;\n }\n\n // allow id fields for relations, media, and components\n const canUseID =\n isRelationalAttribute(parent?.attribute) ||\n isMediaAttribute(parent?.attribute) ||\n isComponentAttribute(parent?.attribute);\n if (canUseID && ID_FIELDS.includes(key)) {\n return;\n }\n\n // if we couldn't find any reason for it to be here, remove it\n remove(key);\n};\n\nexport default removeUnrecognizedFields;\n"],"names":["removeUnrecognizedFields","key","attribute","path","schema","parent","allowedExtraRootKeys","remove","ID_FIELDS","includes","isMorphToRelationalAttribute","MORPH_TO_KEYS","isComponentSchema","isDynamicZoneAttribute","DYNAMIC_ZONE_KEYS","isRelationalAttribute","isMediaAttribute","RELATION_OPERATION_KEYS","canUseID","isComponentAttribute"],"mappings":";;;;AAcA,MAAMA,2BAAoC,CACxC,EAAEC,GAAG,EAAEC,SAAS,EAAEC,IAAI,EAAEC,MAAM,EAAEC,MAAM,EAAEC,oBAAoB,EAAE,EAC9D,EAAEC,MAAM,EAAE,GAAA;;AAGV,IAAA,IAAIL,SAAW,EAAA;AACb,QAAA;AACF;;IAGA,IAAIC,IAAAA,CAAKD,SAAS,KAAK,IAAM,EAAA;QAC3B,IAAIM,sBAAAA,CAAUC,QAAQ,CAACR,GAAM,CAAA,EAAA;AAC3B,YAAA;AACF;QACA,IAAIK,oBAAAA,EAAsBG,SAASR,GAAM,CAAA,EAAA;AACvC,YAAA;AACF;QAEAM,MAAON,CAAAA,GAAAA,CAAAA;AACP,QAAA;AACF;;AAGA,IAAA,IAAIS,0CAA6BL,MAAQH,EAAAA,SAAAA,CAAAA,IAAcS,0BAAcF,CAAAA,QAAQ,CAACR,GAAM,CAAA,EAAA;AAClF,QAAA;AACF;;IAGA,IACEW,8BAAAA,CAAkBR,WAClBS,mCAAuBR,CAAAA,MAAAA,EAAQH,cAC/BY,8BAAkBL,CAAAA,QAAQ,CAACR,GAC3B,CAAA,EAAA;AACA,QAAA;AACF;;AAGA,IAAA,IACE,CAACc,kCAAsBV,CAAAA,MAAAA,EAAQH,SAAcc,CAAAA,IAAAA,6BAAAA,CAAiBX,MAAQH,EAAAA,SAAAA,CAAS,KAC/Ee,oCAAAA,CAAwBR,QAAQ,CAACR,GACjC,CAAA,EAAA;AACA,QAAA;AACF;;IAGA,MAAMiB,QAAAA,GACJH,mCAAsBV,MAAQH,EAAAA,SAAAA,CAAAA,IAC9Bc,8BAAiBX,MAAQH,EAAAA,SAAAA,CAAAA,IACzBiB,kCAAqBd,MAAQH,EAAAA,SAAAA,CAAAA;AAC/B,IAAA,IAAIgB,QAAYV,IAAAA,sBAAAA,CAAUC,QAAQ,CAACR,GAAM,CAAA,EAAA;AACvC,QAAA;AACF;;IAGAM,MAAON,CAAAA,GAAAA,CAAAA;AACT;;;;"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ID_FIELDS, isMorphToRelationalAttribute, MORPH_TO_KEYS, isComponentSchema, isDynamicZoneAttribute, DYNAMIC_ZONE_KEYS, isRelationalAttribute, isMediaAttribute, RELATION_OPERATION_KEYS, isComponentAttribute } from '../../content-types.mjs';
|
|
2
|
+
|
|
3
|
+
const removeUnrecognizedFields = ({ key, attribute, path, schema, parent, allowedExtraRootKeys }, { remove })=>{
|
|
4
|
+
// We only look at properties that are not attributes
|
|
5
|
+
if (attribute) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
// At root level (path.attribute === null), only accept id-like fields
|
|
9
|
+
if (path.attribute === null) {
|
|
10
|
+
if (ID_FIELDS.includes(key)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (allowedExtraRootKeys?.includes(key)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
remove(key);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// allow special morphTo keys
|
|
20
|
+
if (isMorphToRelationalAttribute(parent?.attribute) && MORPH_TO_KEYS.includes(key)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// allow special dz keys
|
|
24
|
+
if (isComponentSchema(schema) && isDynamicZoneAttribute(parent?.attribute) && DYNAMIC_ZONE_KEYS.includes(key)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// allow relation operation keys (connect, disconnect, set, options) for relations and media
|
|
28
|
+
if ((isRelationalAttribute(parent?.attribute) || isMediaAttribute(parent?.attribute)) && RELATION_OPERATION_KEYS.includes(key)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// allow id fields for relations, media, and components
|
|
32
|
+
const canUseID = isRelationalAttribute(parent?.attribute) || isMediaAttribute(parent?.attribute) || isComponentAttribute(parent?.attribute);
|
|
33
|
+
if (canUseID && ID_FIELDS.includes(key)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// if we couldn't find any reason for it to be here, remove it
|
|
37
|
+
remove(key);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export { removeUnrecognizedFields as default };
|
|
41
|
+
//# sourceMappingURL=remove-unrecognized-fields.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove-unrecognized-fields.mjs","sources":["../../../src/sanitize/visitors/remove-unrecognized-fields.ts"],"sourcesContent":["import {\n isDynamicZoneAttribute,\n isMorphToRelationalAttribute,\n isRelationalAttribute,\n isComponentSchema,\n isMediaAttribute,\n isComponentAttribute,\n DYNAMIC_ZONE_KEYS,\n ID_FIELDS,\n MORPH_TO_KEYS,\n RELATION_OPERATION_KEYS,\n} from '../../content-types';\nimport type { Visitor } from '../../traverse-entity';\n\nconst removeUnrecognizedFields: Visitor = (\n { key, attribute, path, schema, parent, allowedExtraRootKeys },\n { remove }\n) => {\n // We only look at properties that are not attributes\n if (attribute) {\n return;\n }\n\n // At root level (path.attribute === null), only accept id-like fields\n if (path.attribute === null) {\n if (ID_FIELDS.includes(key)) {\n return;\n }\n if (allowedExtraRootKeys?.includes(key)) {\n return;\n }\n\n remove(key);\n return;\n }\n\n // allow special morphTo keys\n if (isMorphToRelationalAttribute(parent?.attribute) && MORPH_TO_KEYS.includes(key)) {\n return;\n }\n\n // allow special dz keys\n if (\n isComponentSchema(schema) &&\n isDynamicZoneAttribute(parent?.attribute) &&\n DYNAMIC_ZONE_KEYS.includes(key)\n ) {\n return;\n }\n\n // allow relation operation keys (connect, disconnect, set, options) for relations and media\n if (\n (isRelationalAttribute(parent?.attribute) || isMediaAttribute(parent?.attribute)) &&\n RELATION_OPERATION_KEYS.includes(key)\n ) {\n return;\n }\n\n // allow id fields for relations, media, and components\n const canUseID =\n isRelationalAttribute(parent?.attribute) ||\n isMediaAttribute(parent?.attribute) ||\n isComponentAttribute(parent?.attribute);\n if (canUseID && ID_FIELDS.includes(key)) {\n return;\n }\n\n // if we couldn't find any reason for it to be here, remove it\n remove(key);\n};\n\nexport default removeUnrecognizedFields;\n"],"names":["removeUnrecognizedFields","key","attribute","path","schema","parent","allowedExtraRootKeys","remove","ID_FIELDS","includes","isMorphToRelationalAttribute","MORPH_TO_KEYS","isComponentSchema","isDynamicZoneAttribute","DYNAMIC_ZONE_KEYS","isRelationalAttribute","isMediaAttribute","RELATION_OPERATION_KEYS","canUseID","isComponentAttribute"],"mappings":";;AAcA,MAAMA,2BAAoC,CACxC,EAAEC,GAAG,EAAEC,SAAS,EAAEC,IAAI,EAAEC,MAAM,EAAEC,MAAM,EAAEC,oBAAoB,EAAE,EAC9D,EAAEC,MAAM,EAAE,GAAA;;AAGV,IAAA,IAAIL,SAAW,EAAA;AACb,QAAA;AACF;;IAGA,IAAIC,IAAAA,CAAKD,SAAS,KAAK,IAAM,EAAA;QAC3B,IAAIM,SAAAA,CAAUC,QAAQ,CAACR,GAAM,CAAA,EAAA;AAC3B,YAAA;AACF;QACA,IAAIK,oBAAAA,EAAsBG,SAASR,GAAM,CAAA,EAAA;AACvC,YAAA;AACF;QAEAM,MAAON,CAAAA,GAAAA,CAAAA;AACP,QAAA;AACF;;AAGA,IAAA,IAAIS,6BAA6BL,MAAQH,EAAAA,SAAAA,CAAAA,IAAcS,aAAcF,CAAAA,QAAQ,CAACR,GAAM,CAAA,EAAA;AAClF,QAAA;AACF;;IAGA,IACEW,iBAAAA,CAAkBR,WAClBS,sBAAuBR,CAAAA,MAAAA,EAAQH,cAC/BY,iBAAkBL,CAAAA,QAAQ,CAACR,GAC3B,CAAA,EAAA;AACA,QAAA;AACF;;AAGA,IAAA,IACE,CAACc,qBAAsBV,CAAAA,MAAAA,EAAQH,SAAcc,CAAAA,IAAAA,gBAAAA,CAAiBX,MAAQH,EAAAA,SAAAA,CAAS,KAC/Ee,uBAAAA,CAAwBR,QAAQ,CAACR,GACjC,CAAA,EAAA;AACA,QAAA;AACF;;IAGA,MAAMiB,QAAAA,GACJH,sBAAsBV,MAAQH,EAAAA,SAAAA,CAAAA,IAC9Bc,iBAAiBX,MAAQH,EAAAA,SAAAA,CAAAA,IACzBiB,qBAAqBd,MAAQH,EAAAA,SAAAA,CAAAA;AAC/B,IAAA,IAAIgB,QAAYV,IAAAA,SAAAA,CAAUC,QAAQ,CAACR,GAAM,CAAA,EAAA;AACvC,QAAA;AACF;;IAGAM,MAAON,CAAAA,GAAAA,CAAAA;AACT;;;;"}
|
|
@@ -10,6 +10,8 @@ export interface VisitorOptions {
|
|
|
10
10
|
path: Path;
|
|
11
11
|
getModel(uid: string): Model;
|
|
12
12
|
parent?: Parent;
|
|
13
|
+
/** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */
|
|
14
|
+
allowedExtraRootKeys?: string[];
|
|
13
15
|
}
|
|
14
16
|
export type Visitor = (visitorOptions: VisitorOptions, visitorUtils: VisitorUtils) => void;
|
|
15
17
|
export interface Path {
|
|
@@ -22,6 +24,8 @@ export interface TraverseOptions {
|
|
|
22
24
|
path?: Path;
|
|
23
25
|
parent?: Parent;
|
|
24
26
|
getModel(uid: string): Model;
|
|
27
|
+
/** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */
|
|
28
|
+
allowedExtraRootKeys?: string[];
|
|
25
29
|
}
|
|
26
30
|
export interface Parent {
|
|
27
31
|
attribute?: Attribute;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traverse-entity.d.ts","sourceRoot":"","sources":["../src/traverse-entity.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAoBpE,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,KAAK,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACxB,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"traverse-entity.d.ts","sourceRoot":"","sources":["../src/traverse-entity.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAoBpE,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,KAAK,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACxB,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4GAA4G;IAC5G,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,KAAK,IAAI,CAAC;AAE3F,MAAM,WAAW,IAAI;IACnB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B,4GAA4G;IAC5G,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,MAAM;IACrB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,KAAK,CAAC;CACf;AAiOD,QAAA,MAAM,kBAAkB,aAAc;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE;gBACtC,MAAM;aAIT,MAAM,SAAS,IAAI;CAG5B,CAAC;;AAEH,wBAAqC"}
|