@strapi/utils 5.52.2 → 5.53.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.
Files changed (31) hide show
  1. package/dist/convert-query-params.d.ts.map +1 -1
  2. package/dist/convert-query-params.js +31 -1
  3. package/dist/convert-query-params.js.map +1 -1
  4. package/dist/convert-query-params.mjs +31 -1
  5. package/dist/convert-query-params.mjs.map +1 -1
  6. package/dist/sanitize/index.d.ts.map +1 -1
  7. package/dist/sanitize/index.js +3 -1
  8. package/dist/sanitize/index.js.map +1 -1
  9. package/dist/sanitize/index.mjs +3 -1
  10. package/dist/sanitize/index.mjs.map +1 -1
  11. package/dist/sanitize/visitors/remove-restricted-relations.d.ts.map +1 -1
  12. package/dist/sanitize/visitors/remove-restricted-relations.js +93 -20
  13. package/dist/sanitize/visitors/remove-restricted-relations.js.map +1 -1
  14. package/dist/sanitize/visitors/remove-restricted-relations.mjs +94 -21
  15. package/dist/sanitize/visitors/remove-restricted-relations.mjs.map +1 -1
  16. package/dist/traverse/query-populate.d.ts.map +1 -1
  17. package/dist/traverse/query-populate.js +23 -8
  18. package/dist/traverse/query-populate.js.map +1 -1
  19. package/dist/traverse/query-populate.mjs +24 -9
  20. package/dist/traverse/query-populate.mjs.map +1 -1
  21. package/dist/validate/index.d.ts.map +1 -1
  22. package/dist/validate/index.js +2 -8
  23. package/dist/validate/index.js.map +1 -1
  24. package/dist/validate/index.mjs +2 -8
  25. package/dist/validate/index.mjs.map +1 -1
  26. package/dist/validate/visitors/throw-restricted-relations.d.ts.map +1 -1
  27. package/dist/validate/visitors/throw-restricted-relations.js +34 -6
  28. package/dist/validate/visitors/throw-restricted-relations.js.map +1 -1
  29. package/dist/validate/visitors/throw-restricted-relations.mjs +35 -7
  30. package/dist/validate/visitors/throw-restricted-relations.mjs.map +1 -1
  31. package/package.json +4 -4
@@ -1 +1 @@
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';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\nimport { hasSort } from '../sort-query';\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: unknown[] = Array.from({ length: 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 ('publicationFilter' in sanitizedQuery) {\n validatePublicationFilterQueryParam(sanitizedQuery.publicationFilter);\n }\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (hasSort(sort)) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n } else if ('sort' in sanitizedQuery) {\n delete sanitizedQuery.sort;\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","from","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","validatePublicationFilterQueryParam","publicationFilter","assign","sanitizeFilters","hasSort","sanitizeSort","sanitizeFields","sanitizePopulate","extraQueryKeys","getExtraQueryKeysFromRoute","routeQuerySchema","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","pick","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';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\nimport { hasSort } from '../sort-query';\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: unknown[] = Array.from({ length: 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 ('publicationFilter' in sanitizedQuery) {\n validatePublicationFilterQueryParam(sanitizedQuery.publicationFilter);\n }\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (hasSort(sort)) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n } else if ('sort' in sanitizedQuery) {\n delete sanitizedQuery.sort;\n }\n\n if (fields) {\n Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields, schema) });\n }\n\n if (populate) {\n Object.assign(sanitizedQuery, {\n populate: await sanitizePopulate(populate, schema, { auth }),\n });\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","from","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","validatePublicationFilterQueryParam","publicationFilter","assign","sanitizeFilters","hasSort","sanitizeSort","sanitizeFields","sanitizePopulate","extraQueryKeys","getExtraQueryKeysFromRoute","routeQuerySchema","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","pick","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
@@ -158,7 +158,9 @@ const createAPISanitizers = (opts)=>{
158
158
  }
159
159
  if (populate) {
160
160
  Object.assign(sanitizedQuery, {
161
- populate: await sanitizePopulate(populate, schema)
161
+ populate: await sanitizePopulate(populate, schema, {
162
+ auth
163
+ })
162
164
  });
163
165
  }
164
166
  const extraQueryKeys = getExtraQueryKeysFromRoute(route);
@@ -1 +1 @@
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';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\nimport { hasSort } from '../sort-query';\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: unknown[] = Array.from({ length: 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 ('publicationFilter' in sanitizedQuery) {\n validatePublicationFilterQueryParam(sanitizedQuery.publicationFilter);\n }\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (hasSort(sort)) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n } else if ('sort' in sanitizedQuery) {\n delete sanitizedQuery.sort;\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","from","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","validatePublicationFilterQueryParam","publicationFilter","assign","sanitizeFilters","hasSort","sanitizeSort","sanitizeFields","sanitizePopulate","extraQueryKeys","getExtraQueryKeysFromRoute","routeQuerySchema","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","pick","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
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';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\nimport { hasSort } from '../sort-query';\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: unknown[] = Array.from({ length: 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 ('publicationFilter' in sanitizedQuery) {\n validatePublicationFilterQueryParam(sanitizedQuery.publicationFilter);\n }\n\n if (filters) {\n Object.assign(sanitizedQuery, { filters: await sanitizeFilters(filters, schema, { auth }) });\n }\n\n if (hasSort(sort)) {\n Object.assign(sanitizedQuery, { sort: await sanitizeSort(sort, schema, { auth }) });\n } else if ('sort' in sanitizedQuery) {\n delete sanitizedQuery.sort;\n }\n\n if (fields) {\n Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields, schema) });\n }\n\n if (populate) {\n Object.assign(sanitizedQuery, {\n populate: await sanitizePopulate(populate, schema, { auth }),\n });\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","from","length","i","output","sanitizeQuery","query","filters","sort","fields","populate","sanitizedQuery","cloneDeep","validatePublicationFilterQueryParam","publicationFilter","assign","sanitizeFilters","hasSort","sanitizeSort","sanitizeFields","sanitizePopulate","extraQueryKeys","getExtraQueryKeysFromRoute","routeQuerySchema","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","pick","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"remove-restricted-relations.d.ts","sourceRoot":"","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;yBAStC,MAAM,OAAO,KAAG,OAAO;AAAvC,wBAcI"}
1
+ {"version":3,"file":"remove-restricted-relations.d.ts","sourceRoot":"","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;yBAmBtC,MAAM,OAAO,KAAG,OAAO;AAAvC,wBAcI"}
@@ -30,7 +30,7 @@ const visitRelationAttribute = async (auth, { data, key, attribute, schema }, {
30
30
  if (!elements) {
31
31
  return;
32
32
  }
33
- if ('connect' in elements || 'set' in elements || 'disconnect' in elements) {
33
+ if (isMorphMutationPayload(elements)) {
34
34
  const newValue = {};
35
35
  const connect = await handleMorphElements(elements.connect || []);
36
36
  const relSet = await handleMorphElements(elements.set || []);
@@ -45,36 +45,88 @@ const visitRelationAttribute = async (auth, { data, key, attribute, schema }, {
45
45
  newValue.disconnect = disconnect;
46
46
  }
47
47
  // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here
48
- if ('options' in elements && typeof elements.options === 'object' && elements.options !== null) {
48
+ const options = elements.options;
49
+ if (options && typeof options === 'object') {
49
50
  const filteredOptions = {};
50
- // Iterate through the keys of elements.options
51
- Object.keys(elements.options).forEach((key)=>{
52
- const validator = relations.VALID_RELATION_ORDERING_KEYS[key];
53
- // Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it
54
- if (validator && validator(elements.options[key])) {
55
- filteredOptions[key] = elements.options[key];
51
+ Object.keys(options).forEach((key)=>{
52
+ const optionKey = key;
53
+ const validator = relations.VALID_RELATION_ORDERING_KEYS[optionKey];
54
+ const optionValue = options[key];
55
+ if (validator && validator(optionValue)) {
56
+ filteredOptions[optionKey] = optionValue;
56
57
  }
57
58
  });
58
- // Assign the filtered options back to newValue
59
59
  newValue.options = filteredOptions;
60
60
  } else {
61
61
  newValue.options = {};
62
62
  }
63
63
  set(key, newValue);
64
+ } else if (isMorphPopulatePayload(elements)) {
65
+ const newOn = {};
66
+ for (const [uid, subPopulate] of Object.entries(elements.on)){
67
+ const scopes = ACTIONS_TO_VERIFY.map((action)=>`${uid}.${action}`);
68
+ const isAllowed = await hasAccessToSomeScopes(scopes, auth);
69
+ if (isAllowed) {
70
+ newOn[uid] = subPopulate;
71
+ }
72
+ }
73
+ if (Object.keys(newOn).length === 0) {
74
+ remove(key);
75
+ return;
76
+ }
77
+ const count = isMorphPopulateCount(elements.count) ? true : elements.count;
78
+ set(key, {
79
+ ...elements,
80
+ count,
81
+ on: newOn
82
+ });
64
83
  } else {
65
84
  const newMorphValue = await handleMorphElements(elements);
66
- if (newMorphValue.length) {
85
+ if (!newMorphValue.length) {
86
+ if (fp.isArray(elements) && elements.length === 0) {
87
+ return;
88
+ }
89
+ remove(key);
90
+ return;
91
+ }
92
+ if (fp.isArray(elements)) {
67
93
  set(key, newMorphValue);
94
+ return;
68
95
  }
96
+ set(key, newMorphValue[0]);
69
97
  }
70
98
  };
99
+ const isMorphMutationPayload = (value)=>{
100
+ return fp.isObject(value) && ('connect' in value || 'set' in value || 'disconnect' in value);
101
+ };
102
+ const isMorphPopulateCount = (value)=>value === true || value === 'true';
103
+ const isMorphPopulatePayload = (value)=>{
104
+ return fp.isObject(value) && !('__type' in value) && 'on' in value && fp.isObject(value.on);
105
+ };
106
+ const buildAllowedMorphPopulateFragment = async (subPopulate)=>{
107
+ const newOn = {};
108
+ for (const uid of getRegisteredContentTypeUIDs()){
109
+ const scopes = ACTIONS_TO_VERIFY.map((action)=>`${uid}.${action}`);
110
+ const isAllowed = await hasAccessToSomeScopes(scopes, auth);
111
+ if (isAllowed) {
112
+ newOn[uid] = subPopulate;
113
+ }
114
+ }
115
+ return newOn;
116
+ };
117
+ const isMorphPopulateAllOrCount = (value)=>{
118
+ return value === true || value === 'true' || fp.isObject(value) && 'count' in value && isMorphPopulateCount(value.count) && !('on' in value);
119
+ };
120
+ const isMorphCountOutput = (value)=>{
121
+ return fp.isObject(value) && 'count' in value && typeof value.count === 'number' && Object.keys(value).length === 1;
122
+ };
71
123
  const handleMorphElements = async (elements)=>{
72
124
  const allowedElements = [];
73
- if (!fp.isArray(elements)) {
74
- return allowedElements;
75
- }
76
- for (const element of elements){
77
- if (!fp.isObject(element) || !('__type' in element)) {
125
+ const elementsToCheck = fp.isArray(elements) ? elements : [
126
+ elements
127
+ ];
128
+ for (const element of elementsToCheck){
129
+ if (!fp.isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {
78
130
  continue;
79
131
  }
80
132
  const scopes = ACTIONS_TO_VERIFY.map((action)=>`${element.__type}.${action}`);
@@ -88,7 +140,6 @@ const visitRelationAttribute = async (auth, { data, key, attribute, schema }, {
88
140
  const handleRegularRelation = async ()=>{
89
141
  const scopes = ACTIONS_TO_VERIFY.map((action)=>`${attribute.target}.${action}`);
90
142
  const isAllowed = await hasAccessToSomeScopes(scopes, auth);
91
- // If the authenticated user don't have access to any of the scopes, then remove the field
92
143
  if (!isAllowed) {
93
144
  remove(key);
94
145
  }
@@ -97,17 +148,36 @@ const visitRelationAttribute = async (auth, { data, key, attribute, schema }, {
97
148
  CREATED_BY_ATTRIBUTE,
98
149
  UPDATED_BY_ATTRIBUTE
99
150
  ].includes(key);
100
- // Polymorphic relations
101
151
  if (contentTypes.isMorphToRelationalAttribute(attribute)) {
152
+ const value = data[key];
153
+ if (isMorphPopulatePayload(value)) {
154
+ await handleMorphRelation();
155
+ return;
156
+ }
157
+ if (isMorphPopulateAllOrCount(value)) {
158
+ const newOn = await buildAllowedMorphPopulateFragment(true);
159
+ if (Object.keys(newOn).length === 0) {
160
+ remove(key);
161
+ return;
162
+ }
163
+ set(key, value === true || value === 'true' ? {
164
+ on: newOn
165
+ } : {
166
+ ...value,
167
+ count: true,
168
+ on: newOn
169
+ });
170
+ return;
171
+ }
172
+ if (isMorphCountOutput(value)) {
173
+ return;
174
+ }
102
175
  await handleMorphRelation();
103
176
  return;
104
177
  }
105
- // Creator relations
106
178
  if (isCreatorRelation && schema.options?.populateCreatorFields) {
107
- // do nothing
108
179
  return;
109
180
  }
110
- // Regular relations
111
181
  await handleRegularRelation();
112
182
  };
113
183
  /**
@@ -156,6 +226,9 @@ const hasAccessToSomeScopes = async (scopes, auth)=>{
156
226
  }
157
227
  return false;
158
228
  };
229
+ const getRegisteredContentTypeUIDs = ()=>{
230
+ return Object.values(strapi.contentTypes ?? {}).map((model)=>model.uid).filter((uid)=>typeof uid === 'string');
231
+ };
159
232
 
160
233
  module.exports = removeRestrictedRelations;
161
234
  //# sourceMappingURL=remove-restricted-relations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"remove-restricted-relations.js","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport type { Visitor } from '../../traverse/factory';\nimport { RelationOrderingOptions } from '../../types';\nimport { VALID_RELATION_ORDERING_KEYS } from '../../relations';\n\nconst ACTIONS_TO_VERIFY = ['find'];\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypeUtils.constants;\n\ntype MorphArray = Array<{ __type: string }>;\n\nexport default (auth: unknown): Visitor =>\n (visitorOptions, visitorUtils) => {\n const { attribute } = visitorOptions;\n\n // Deliberately not an `async` function. This visitor is invoked for every key of\n // every node of every entity, and only relation attributes need any work at all.\n // Declaring it `async` meant each of those keys allocated a promise purely to\n // resolve it immediately, and the traversal then awaited it. Returning early\n // without a promise lets the traversal stay synchronous for the common case.\n if (!attribute || attribute.type !== 'relation') {\n return;\n }\n\n return visitRelationAttribute(auth, visitorOptions, visitorUtils) as unknown as void;\n };\n\nconst visitRelationAttribute = async (\n auth: unknown,\n { data, key, attribute, schema }: Parameters<Visitor>[0],\n { remove, set }: Parameters<Visitor>[1]\n) => {\n // Narrowing only; the caller reaches this for relation attributes exclusively.\n if (!attribute) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements: any = (data as Record<string, MorphArray>)[key];\n\n if (!elements) {\n return;\n }\n\n if ('connect' in elements || 'set' in elements || 'disconnect' in elements) {\n const newValue: Record<string, unknown> = {};\n\n const connect = await handleMorphElements(elements.connect || []);\n const relSet = await handleMorphElements(elements.set || []);\n const disconnect = await handleMorphElements(elements.disconnect || []);\n\n if (connect.length > 0) {\n newValue.connect = connect;\n }\n\n if (relSet.length > 0) {\n newValue.set = relSet;\n }\n\n if (disconnect.length > 0) {\n newValue.disconnect = disconnect;\n }\n\n // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here\n if (\n 'options' in elements &&\n typeof elements.options === 'object' &&\n elements.options !== null\n ) {\n const filteredOptions: RelationOrderingOptions = {};\n\n // Iterate through the keys of elements.options\n Object.keys(elements.options).forEach((key) => {\n const validator = VALID_RELATION_ORDERING_KEYS[key as keyof RelationOrderingOptions];\n\n // Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it\n if (validator && validator(elements.options[key])) {\n filteredOptions[key as keyof RelationOrderingOptions] = elements.options[key];\n }\n });\n\n // Assign the filtered options back to newValue\n newValue.options = filteredOptions;\n } else {\n newValue.options = {};\n }\n\n set(key, newValue);\n } else {\n const newMorphValue = await handleMorphElements(elements);\n\n if (newMorphValue.length) {\n set(key, newMorphValue);\n }\n }\n };\n\n const handleMorphElements = async (elements: any[]) => {\n const allowedElements: Record<string, unknown>[] = [];\n\n if (!isArray(elements)) {\n return allowedElements;\n }\n\n for (const element of elements) {\n if (!isObject(element) || !('__type' in element)) {\n continue;\n }\n\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n allowedElements.push(element);\n }\n }\n\n return allowedElements;\n };\n\n const handleRegularRelation = async () => {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);\n\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n // If the authenticated user don't have access to any of the scopes, then remove the field\n if (!isAllowed) {\n remove(key);\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n // Polymorphic relations\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n await handleMorphRelation();\n return;\n }\n\n // Creator relations\n if (isCreatorRelation && schema.options?.populateCreatorFields) {\n // do nothing\n return;\n }\n\n // Regular relations\n await handleRegularRelation();\n};\n\n/**\n * Per-`auth` memo of scope decisions.\n *\n * The decision for a given scope depends only on the ability carried by `auth`, which is\n * built once per request and not mutated while the response is sanitized. Sanitizing a\n * list re-asks the same question for every entity, so a 25-entity page with four\n * relations asked ~100 identical questions; each denial also constructed an Error, and\n * capturing a stack trace is by far the most expensive part of that.\n *\n * Keyed weakly on the `auth` object so entries are collected with the request.\n */\nconst scopeDecisionCache = new WeakMap<object, Map<string, boolean>>();\n\nconst canAccessScope = async (scope: string, auth: unknown): Promise<boolean> => {\n const cacheable = typeof auth === 'object' && auth !== null;\n\n let decisions: Map<string, boolean> | undefined;\n\n if (cacheable) {\n decisions = scopeDecisionCache.get(auth as object);\n\n if (decisions === undefined) {\n decisions = new Map();\n scopeDecisionCache.set(auth as object, decisions);\n } else {\n const cached = decisions.get(scope);\n if (cached !== undefined) {\n return cached;\n }\n }\n }\n\n let allowed: boolean;\n try {\n await strapi.auth.verify(auth, { scope });\n allowed = true;\n } catch {\n allowed = false;\n }\n\n decisions?.set(scope, allowed);\n\n return allowed;\n};\n\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n if (await canAccessScope(scope, auth)) {\n return true;\n }\n }\n\n return false;\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","visitorOptions","visitorUtils","attribute","type","visitRelationAttribute","data","key","schema","remove","set","handleMorphRelation","elements","newValue","connect","handleMorphElements","relSet","disconnect","length","options","filteredOptions","Object","keys","forEach","validator","VALID_RELATION_ORDERING_KEYS","newMorphValue","allowedElements","isArray","element","isObject","scopes","map","action","__type","isAllowed","hasAccessToSomeScopes","push","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scopeDecisionCache","WeakMap","canAccessScope","scope","cacheable","decisions","get","undefined","Map","cached","allowed","strapi","verify"],"mappings":"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"}
1
+ {"version":3,"file":"remove-restricted-relations.js","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport type { Model, RelationOrderingOptions } from '../../types';\nimport type { Visitor } from '../../traverse/factory';\nimport { VALID_RELATION_ORDERING_KEYS } from '../../relations';\n\nconst ACTIONS_TO_VERIFY = ['find'];\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypeUtils.constants;\n\ntype MorphArray = Array<{ __type: string }>;\ntype MorphElement = Record<string, unknown> & { __type: string };\ntype MorphMutationPayload = {\n connect?: unknown;\n set?: unknown;\n disconnect?: unknown;\n options?: Record<string, unknown> | null;\n};\ntype MorphPopulatePayload = {\n on: Record<string, unknown>;\n count?: unknown;\n};\n\nexport default (auth: unknown): Visitor =>\n (visitorOptions, visitorUtils) => {\n const { attribute } = visitorOptions;\n\n // Deliberately not an `async` function. This visitor is invoked for every key of\n // every node of every entity, and only relation attributes need any work at all.\n // Declaring it `async` meant each of those keys allocated a promise purely to\n // resolve it immediately, and the traversal then awaited it. Returning early\n // without a promise lets the traversal stay synchronous for the common case.\n if (!attribute || attribute.type !== 'relation') {\n return;\n }\n\n return visitRelationAttribute(auth, visitorOptions, visitorUtils) as unknown as void;\n };\n\nconst visitRelationAttribute = async (\n auth: unknown,\n { data, key, attribute, schema }: Parameters<Visitor>[0],\n { remove, set }: Parameters<Visitor>[1]\n) => {\n // Narrowing only; the caller reaches this for relation attributes exclusively.\n if (!attribute) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements = (data as Record<string, MorphArray | MorphElement | MorphMutationPayload>)[\n key\n ];\n\n if (!elements) {\n return;\n }\n\n if (isMorphMutationPayload(elements)) {\n const newValue: Record<string, unknown> = {};\n\n const connect = await handleMorphElements(elements.connect || []);\n const relSet = await handleMorphElements(elements.set || []);\n const disconnect = await handleMorphElements(elements.disconnect || []);\n\n if (connect.length > 0) {\n newValue.connect = connect;\n }\n\n if (relSet.length > 0) {\n newValue.set = relSet;\n }\n\n if (disconnect.length > 0) {\n newValue.disconnect = disconnect;\n }\n\n // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here\n const options = elements.options;\n if (options && typeof options === 'object') {\n const filteredOptions: RelationOrderingOptions = {};\n\n Object.keys(options).forEach((key) => {\n const optionKey = key as keyof RelationOrderingOptions;\n const validator = VALID_RELATION_ORDERING_KEYS[optionKey];\n const optionValue = options[key];\n\n if (validator && validator(optionValue)) {\n filteredOptions[optionKey] = optionValue as RelationOrderingOptions[typeof optionKey];\n }\n });\n\n newValue.options = filteredOptions;\n } else {\n newValue.options = {};\n }\n\n set(key, newValue);\n } else if (isMorphPopulatePayload(elements)) {\n const newOn: Record<string, unknown> = {};\n\n for (const [uid, subPopulate] of Object.entries(elements.on)) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n newOn[uid] = subPopulate;\n }\n }\n\n if (Object.keys(newOn).length === 0) {\n remove(key);\n return;\n }\n\n const count = isMorphPopulateCount(elements.count) ? true : elements.count;\n set(key, { ...elements, count, on: newOn });\n } else {\n const newMorphValue = await handleMorphElements(elements);\n\n if (!newMorphValue.length) {\n if (isArray(elements) && elements.length === 0) {\n return;\n }\n\n remove(key);\n return;\n }\n\n if (isArray(elements)) {\n set(key, newMorphValue);\n return;\n }\n\n set(key, newMorphValue[0]);\n }\n };\n\n const isMorphMutationPayload = (value: unknown): value is MorphMutationPayload => {\n return isObject(value) && ('connect' in value || 'set' in value || 'disconnect' in value);\n };\n\n const isMorphPopulateCount = (value: unknown) => value === true || value === 'true';\n\n const isMorphPopulatePayload = (value: unknown): value is MorphPopulatePayload => {\n return isObject(value) && !('__type' in value) && 'on' in value && isObject(value.on);\n };\n\n const buildAllowedMorphPopulateFragment = async (subPopulate: unknown) => {\n const newOn: Record<string, unknown> = {};\n\n for (const uid of getRegisteredContentTypeUIDs()) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n newOn[uid] = subPopulate;\n }\n }\n\n return newOn;\n };\n\n const isMorphPopulateAllOrCount = (value: unknown): value is true | 'true' | { count: true } => {\n return (\n value === true ||\n value === 'true' ||\n (isObject(value) && 'count' in value && isMorphPopulateCount(value.count) && !('on' in value))\n );\n };\n\n const isMorphCountOutput = (value: unknown): value is { count: number } => {\n return (\n isObject(value) &&\n 'count' in value &&\n typeof value.count === 'number' &&\n Object.keys(value).length === 1\n );\n };\n\n const handleMorphElements = async (elements: unknown) => {\n const allowedElements: Record<string, unknown>[] = [];\n const elementsToCheck = isArray(elements) ? elements : [elements];\n\n for (const element of elementsToCheck) {\n if (!isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {\n continue;\n }\n\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n allowedElements.push(element);\n }\n }\n\n return allowedElements;\n };\n\n const handleRegularRelation = async () => {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (!isAllowed) {\n remove(key);\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n const value = (data as Record<string, unknown>)[key];\n\n if (isMorphPopulatePayload(value)) {\n await handleMorphRelation();\n return;\n }\n\n if (isMorphPopulateAllOrCount(value)) {\n const newOn = await buildAllowedMorphPopulateFragment(true);\n\n if (Object.keys(newOn).length === 0) {\n remove(key);\n return;\n }\n\n set(\n key,\n value === true || value === 'true' ? { on: newOn } : { ...value, count: true, on: newOn }\n );\n return;\n }\n\n if (isMorphCountOutput(value)) {\n return;\n }\n\n await handleMorphRelation();\n return;\n }\n\n if (isCreatorRelation && schema.options?.populateCreatorFields) {\n return;\n }\n\n await handleRegularRelation();\n};\n\n/**\n * Per-`auth` memo of scope decisions.\n *\n * The decision for a given scope depends only on the ability carried by `auth`, which is\n * built once per request and not mutated while the response is sanitized. Sanitizing a\n * list re-asks the same question for every entity, so a 25-entity page with four\n * relations asked ~100 identical questions; each denial also constructed an Error, and\n * capturing a stack trace is by far the most expensive part of that.\n *\n * Keyed weakly on the `auth` object so entries are collected with the request.\n */\nconst scopeDecisionCache = new WeakMap<object, Map<string, boolean>>();\n\nconst canAccessScope = async (scope: string, auth: unknown): Promise<boolean> => {\n const cacheable = typeof auth === 'object' && auth !== null;\n\n let decisions: Map<string, boolean> | undefined;\n\n if (cacheable) {\n decisions = scopeDecisionCache.get(auth as object);\n\n if (decisions === undefined) {\n decisions = new Map();\n scopeDecisionCache.set(auth as object, decisions);\n } else {\n const cached = decisions.get(scope);\n if (cached !== undefined) {\n return cached;\n }\n }\n }\n\n let allowed: boolean;\n try {\n await strapi.auth.verify(auth, { scope });\n allowed = true;\n } catch {\n allowed = false;\n }\n\n decisions?.set(scope, allowed);\n\n return allowed;\n};\n\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n if (await canAccessScope(scope, auth)) {\n return true;\n }\n }\n\n return false;\n};\n\nconst getRegisteredContentTypeUIDs = () => {\n return (Object.values(strapi.contentTypes ?? {}) as Model[])\n .map((model) => model.uid)\n .filter((uid): uid is string => typeof uid === 'string');\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","visitorOptions","visitorUtils","attribute","type","visitRelationAttribute","data","key","schema","remove","set","handleMorphRelation","elements","isMorphMutationPayload","newValue","connect","handleMorphElements","relSet","disconnect","length","options","filteredOptions","Object","keys","forEach","optionKey","validator","VALID_RELATION_ORDERING_KEYS","optionValue","isMorphPopulatePayload","newOn","uid","subPopulate","entries","on","scopes","map","action","isAllowed","hasAccessToSomeScopes","count","isMorphPopulateCount","newMorphValue","isArray","value","isObject","buildAllowedMorphPopulateFragment","getRegisteredContentTypeUIDs","isMorphPopulateAllOrCount","isMorphCountOutput","allowedElements","elementsToCheck","element","__type","push","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scopeDecisionCache","WeakMap","canAccessScope","scope","cacheable","decisions","get","undefined","Map","cached","allowed","strapi","verify","values","contentTypes","model","filter"],"mappings":"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"}