@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.
- package/dist/convert-query-params.d.ts.map +1 -1
- package/dist/convert-query-params.js +31 -1
- package/dist/convert-query-params.js.map +1 -1
- package/dist/convert-query-params.mjs +31 -1
- package/dist/convert-query-params.mjs.map +1 -1
- package/dist/sanitize/index.d.ts.map +1 -1
- package/dist/sanitize/index.js +3 -1
- package/dist/sanitize/index.js.map +1 -1
- package/dist/sanitize/index.mjs +3 -1
- package/dist/sanitize/index.mjs.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.d.ts.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.js +93 -20
- package/dist/sanitize/visitors/remove-restricted-relations.js.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.mjs +94 -21
- package/dist/sanitize/visitors/remove-restricted-relations.mjs.map +1 -1
- package/dist/traverse/query-populate.d.ts.map +1 -1
- package/dist/traverse/query-populate.js +23 -8
- package/dist/traverse/query-populate.js.map +1 -1
- package/dist/traverse/query-populate.mjs +24 -9
- package/dist/traverse/query-populate.mjs.map +1 -1
- package/dist/validate/index.d.ts.map +1 -1
- package/dist/validate/index.js +2 -8
- package/dist/validate/index.js.map +1 -1
- package/dist/validate/index.mjs +2 -8
- package/dist/validate/index.mjs.map +1 -1
- package/dist/validate/visitors/throw-restricted-relations.d.ts.map +1 -1
- package/dist/validate/visitors/throw-restricted-relations.js +34 -6
- package/dist/validate/visitors/throw-restricted-relations.js.map +1 -1
- package/dist/validate/visitors/throw-restricted-relations.mjs +35 -7
- package/dist/validate/visitors/throw-restricted-relations.mjs.map +1 -1
- package/package.json +4 -4
package/dist/validate/index.js
CHANGED
|
@@ -11,7 +11,7 @@ var validators = require('./validators.js');
|
|
|
11
11
|
var traverseEntity = require('../traverse-entity.js');
|
|
12
12
|
var queryFilters = require('../traverse/query-filters.js');
|
|
13
13
|
var querySort = require('../traverse/query-sort.js');
|
|
14
|
-
|
|
14
|
+
require('../traverse/query-populate.js');
|
|
15
15
|
require('../traverse/query-fields.js');
|
|
16
16
|
var errors = require('../errors.js');
|
|
17
17
|
var publicationFilter = require('../publication-filter.js');
|
|
@@ -257,7 +257,7 @@ const createAPIValidators = (opts)=>{
|
|
|
257
257
|
throw e;
|
|
258
258
|
}
|
|
259
259
|
};
|
|
260
|
-
const validatePopulate = async (populate, schema
|
|
260
|
+
const validatePopulate = async (populate, schema)=>{
|
|
261
261
|
if (!schema) {
|
|
262
262
|
throw new Error('Missing schema in sanitizePopulate');
|
|
263
263
|
}
|
|
@@ -267,12 +267,6 @@ const createAPIValidators = (opts)=>{
|
|
|
267
267
|
getModel
|
|
268
268
|
})
|
|
269
269
|
];
|
|
270
|
-
if (auth) {
|
|
271
|
-
transforms.push(queryPopulate(throwRestrictedRelations(auth), {
|
|
272
|
-
schema,
|
|
273
|
-
getModel
|
|
274
|
-
}));
|
|
275
|
-
}
|
|
276
270
|
try {
|
|
277
271
|
await async.pipe(...transforms)(populate);
|
|
278
272
|
} catch (e) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/validate/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, isObject } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { getNonWritableAttributes, constants } 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';\nimport { throwInvalidKey } from './utils';\n\nimport * as visitors from './visitors';\nimport * as validators from './validators';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\n\nimport { Model, Data } from '../types';\nimport { ValidationError } from '../errors';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\n\nconst { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants;\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, validateQuery throws when the query has any top-level key not in the allowed list.\n * Defaults to false for backward compatibility.\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Validator {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface ValidateFunc {\n (data: unknown, schema: Model, options?: Options): Promise<void>;\n}\n\nexport type ValidateQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<void>;\n\nexport type ValidateBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<void>;\n\ninterface APIOptions {\n validators?: Validators;\n getModel: (model: string) => Model;\n}\n\nexport interface Validators {\n input?: Validator[];\n}\n\nconst createAPIValidators = (opts: APIOptions) => {\n const { getModel } = opts || {};\n\n const validateInput: ValidateFunc = async (data: unknown, schema: Model, options = {}) => {\n const { auth, route } = options;\n if (!schema) {\n throw new Error('Missing schema in validateInput');\n }\n\n if (isArray(data)) {\n await Promise.all(data.map((entry) => validateInput(entry, schema, options)));\n return;\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n (data: unknown) => {\n if (isObject(data)) {\n if (ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: ID_ATTRIBUTE });\n }\n\n if (DOC_ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: DOC_ID_ATTRIBUTE });\n }\n }\n return data;\n },\n // non-writable attributes\n traverseEntity(visitors.throwRestrictedFields(nonWritableAttributes), { schema, getModel }),\n // unrecognized attributes (allowedExtraRootKeys = registered input param keys)\n traverseEntity(visitors.throwUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n }),\n ];\n\n if (auth) {\n // restricted relations\n transforms.push(\n traverseEntity(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n // Apply validators from registry if exists\n opts?.validators?.input?.forEach((validator: Validator) => transforms.push(validator(schema)));\n\n try {\n await pipeAsync(...transforms)(data as Data);\n\n // Validate extra root keys from route's body schema with Zod (throw on failure).\n //\n // Content-api sends the document payload as body.data; the controller calls validateInput(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 validated above by traverseEntity (throwUnrecognizedFields, 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 if (isObject(data) && route?.request?.body?.['application/json']) {\n const bodySchema = route.request.body['application/json'];\n if (typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n const dataObj = data as Record<string, unknown>;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in dataObj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).parse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(dataObj[key]);\n if (!result.success) {\n throw new ValidationError(\n (result.error?.message as string) ?? 'Validation failed',\n { key, path: null, source: 'body', param: key }\n );\n }\n }\n }\n }\n }\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'body';\n }\n throw e;\n }\n };\n\n const validateQuery = 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 validateQuery');\n }\n\n // Core allowlisted params are not covered by route.request.query Zod (see getExtraQueryKeysFromRoute).\n if ('publicationFilter' in query) {\n validatePublicationFilterQueryParam(query.publicationFilter);\n }\n\n if (strictParams) {\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n for (const key of Object.keys(query)) {\n if (!allowedKeys.includes(key)) {\n try {\n throwInvalidKey({ key, path: null });\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = key;\n }\n throw e;\n }\n }\n }\n // Validate extra query keys from route's request schema with Zod (throw on failure)\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).parse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (!result.success) {\n throw new ValidationError(\n (result.error?.message as string) ?? 'Invalid query param',\n { key, path: null, source: 'query', param: key }\n );\n }\n }\n }\n }\n }\n }\n\n const { filters, sort, fields, populate } = query;\n\n if (filters) {\n await validateFilters(filters, schema, { auth });\n }\n\n if (sort) {\n await validateSort(sort, schema, { auth });\n }\n\n if (fields) {\n await validateFields(fields, schema);\n }\n\n // a wildcard is always valid; its conversion will be handled by the entity service and can be optimized with sanitizer\n if (populate && populate !== '*') {\n await validatePopulate(populate, schema);\n }\n };\n\n const validateFilters: ValidateFunc = async (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateFilters');\n }\n if (isArray(filters)) {\n await Promise.all(filters.map((filter) => validateFilters(filter, schema, { auth })));\n return;\n }\n\n const transforms = [validators.defaultValidateFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(filters);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'filters';\n }\n throw e;\n }\n };\n\n const validateSort: ValidateFunc = async (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateSort');\n }\n const transforms = [validators.defaultValidateSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(sort);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'sort';\n }\n throw e;\n }\n };\n\n const validateFields: ValidateFunc = async (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in validateFields');\n }\n const transforms = [validators.defaultValidateFields({ schema, getModel })];\n\n try {\n await pipeAsync(...transforms)(fields);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'fields';\n }\n throw e;\n }\n };\n\n const validatePopulate: ValidateFunc = async (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [validators.defaultValidatePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(populate);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'populate';\n }\n throw e;\n }\n };\n\n return {\n input: validateInput,\n query: validateQuery,\n filters: validateFilters,\n sort: validateSort,\n fields: validateFields,\n populate: validatePopulate,\n };\n};\n\nexport { createAPIValidators, validators, visitors };\n\nexport type APIValidators = ReturnType<typeof createAPIValidators>;\n"],"names":["ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","constants","createAPIValidators","opts","getModel","validateInput","data","schema","options","auth","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","isObject","throwInvalidKey","key","traverseEntity","visitors","push","validators","input","forEach","validator","pipeAsync","request","body","bodySchema","shape","dataObj","Object","keys","zodSchema","parse","result","safeParse","success","ValidationError","error","message","path","source","param","e","details","validateQuery","query","strictParams","validatePublicationFilterQueryParam","publicationFilter","extraQueryKeys","getExtraQueryKeysFromRoute","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","includes","routeQuerySchema","filters","sort","fields","populate","validateFilters","validateSort","validateFields","validatePopulate","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/validate/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, isObject } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { getNonWritableAttributes, constants } 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';\nimport { throwInvalidKey } from './utils';\n\nimport * as visitors from './visitors';\nimport * as validators from './validators';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort } from '../traverse';\n\nimport { Model, Data } from '../types';\nimport { ValidationError } from '../errors';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\n\nconst { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants;\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, validateQuery throws when the query has any top-level key not in the allowed list.\n * Defaults to false for backward compatibility.\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Validator {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface ValidateFunc {\n (data: unknown, schema: Model, options?: Options): Promise<void>;\n}\n\nexport type ValidateQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<void>;\n\nexport type ValidateBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<void>;\n\ninterface APIOptions {\n validators?: Validators;\n getModel: (model: string) => Model;\n}\n\nexport interface Validators {\n input?: Validator[];\n}\n\nconst createAPIValidators = (opts: APIOptions) => {\n const { getModel } = opts || {};\n\n const validateInput: ValidateFunc = async (data: unknown, schema: Model, options = {}) => {\n const { auth, route } = options;\n if (!schema) {\n throw new Error('Missing schema in validateInput');\n }\n\n if (isArray(data)) {\n await Promise.all(data.map((entry) => validateInput(entry, schema, options)));\n return;\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n (data: unknown) => {\n if (isObject(data)) {\n if (ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: ID_ATTRIBUTE });\n }\n\n if (DOC_ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: DOC_ID_ATTRIBUTE });\n }\n }\n return data;\n },\n // non-writable attributes\n traverseEntity(visitors.throwRestrictedFields(nonWritableAttributes), { schema, getModel }),\n // unrecognized attributes (allowedExtraRootKeys = registered input param keys)\n traverseEntity(visitors.throwUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n }),\n ];\n\n if (auth) {\n // restricted relations\n transforms.push(\n traverseEntity(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n // Apply validators from registry if exists\n opts?.validators?.input?.forEach((validator: Validator) => transforms.push(validator(schema)));\n\n try {\n await pipeAsync(...transforms)(data as Data);\n\n // Validate extra root keys from route's body schema with Zod (throw on failure).\n //\n // Content-api sends the document payload as body.data; the controller calls validateInput(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 validated above by traverseEntity (throwUnrecognizedFields, 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 if (isObject(data) && route?.request?.body?.['application/json']) {\n const bodySchema = route.request.body['application/json'];\n if (typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n const dataObj = data as Record<string, unknown>;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in dataObj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).parse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(dataObj[key]);\n if (!result.success) {\n throw new ValidationError(\n (result.error?.message as string) ?? 'Validation failed',\n { key, path: null, source: 'body', param: key }\n );\n }\n }\n }\n }\n }\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'body';\n }\n throw e;\n }\n };\n\n const validateQuery = 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 validateQuery');\n }\n\n // Core allowlisted params are not covered by route.request.query Zod (see getExtraQueryKeysFromRoute).\n if ('publicationFilter' in query) {\n validatePublicationFilterQueryParam(query.publicationFilter);\n }\n\n if (strictParams) {\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n for (const key of Object.keys(query)) {\n if (!allowedKeys.includes(key)) {\n try {\n throwInvalidKey({ key, path: null });\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = key;\n }\n throw e;\n }\n }\n }\n // Validate extra query keys from route's request schema with Zod (throw on failure)\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).parse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (!result.success) {\n throw new ValidationError(\n (result.error?.message as string) ?? 'Invalid query param',\n { key, path: null, source: 'query', param: key }\n );\n }\n }\n }\n }\n }\n }\n\n const { filters, sort, fields, populate } = query;\n\n if (filters) {\n await validateFilters(filters, schema, { auth });\n }\n\n if (sort) {\n await validateSort(sort, schema, { auth });\n }\n\n if (fields) {\n await validateFields(fields, schema);\n }\n\n // a wildcard is always valid; its conversion will be handled by the entity service and can be optimized with sanitizer\n if (populate && populate !== '*') {\n await validatePopulate(populate, schema);\n }\n };\n\n const validateFilters: ValidateFunc = async (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateFilters');\n }\n if (isArray(filters)) {\n await Promise.all(filters.map((filter) => validateFilters(filter, schema, { auth })));\n return;\n }\n\n const transforms = [validators.defaultValidateFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(filters);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'filters';\n }\n throw e;\n }\n };\n\n const validateSort: ValidateFunc = async (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateSort');\n }\n const transforms = [validators.defaultValidateSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(sort);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'sort';\n }\n throw e;\n }\n };\n\n const validateFields: ValidateFunc = async (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in validateFields');\n }\n const transforms = [validators.defaultValidateFields({ schema, getModel })];\n\n try {\n await pipeAsync(...transforms)(fields);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'fields';\n }\n throw e;\n }\n };\n\n const validatePopulate: ValidateFunc = async (populate, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [validators.defaultValidatePopulate({ schema, getModel })];\n\n try {\n await pipeAsync(...transforms)(populate);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'populate';\n }\n throw e;\n }\n };\n\n return {\n input: validateInput,\n query: validateQuery,\n filters: validateFilters,\n sort: validateSort,\n fields: validateFields,\n populate: validatePopulate,\n };\n};\n\nexport { createAPIValidators, validators, visitors };\n\nexport type APIValidators = ReturnType<typeof createAPIValidators>;\n"],"names":["ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","constants","createAPIValidators","opts","getModel","validateInput","data","schema","options","auth","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","isObject","throwInvalidKey","key","traverseEntity","visitors","push","validators","input","forEach","validator","pipeAsync","request","body","bodySchema","shape","dataObj","Object","keys","zodSchema","parse","result","safeParse","success","ValidationError","error","message","path","source","param","e","details","validateQuery","query","strictParams","validatePublicationFilterQueryParam","publicationFilter","extraQueryKeys","getExtraQueryKeysFromRoute","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","includes","routeQuerySchema","filters","sort","fields","populate","validateFilters","validateSort","validateFields","validatePopulate","filter","traverseQueryFilters","traverseQuerySort"],"mappings":"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"}
|
package/dist/validate/index.mjs
CHANGED
|
@@ -12,7 +12,7 @@ export { validators };
|
|
|
12
12
|
import traverseEntity from '../traverse-entity.mjs';
|
|
13
13
|
import traverseQueryFilters from '../traverse/query-filters.mjs';
|
|
14
14
|
import traverseQuerySort from '../traverse/query-sort.mjs';
|
|
15
|
-
import
|
|
15
|
+
import '../traverse/query-populate.mjs';
|
|
16
16
|
import '../traverse/query-fields.mjs';
|
|
17
17
|
import { ValidationError } from '../errors.mjs';
|
|
18
18
|
import { validatePublicationFilterQueryParam } from '../publication-filter.mjs';
|
|
@@ -258,7 +258,7 @@ const createAPIValidators = (opts)=>{
|
|
|
258
258
|
throw e;
|
|
259
259
|
}
|
|
260
260
|
};
|
|
261
|
-
const validatePopulate = async (populate, schema
|
|
261
|
+
const validatePopulate = async (populate, schema)=>{
|
|
262
262
|
if (!schema) {
|
|
263
263
|
throw new Error('Missing schema in sanitizePopulate');
|
|
264
264
|
}
|
|
@@ -268,12 +268,6 @@ const createAPIValidators = (opts)=>{
|
|
|
268
268
|
getModel
|
|
269
269
|
})
|
|
270
270
|
];
|
|
271
|
-
if (auth) {
|
|
272
|
-
transforms.push(traverseQueryPopulate(throwRestrictedRelations(auth), {
|
|
273
|
-
schema,
|
|
274
|
-
getModel
|
|
275
|
-
}));
|
|
276
|
-
}
|
|
277
271
|
try {
|
|
278
272
|
await pipe(...transforms)(populate);
|
|
279
273
|
} catch (e) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../src/validate/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, isObject } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { getNonWritableAttributes, constants } 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';\nimport { throwInvalidKey } from './utils';\n\nimport * as visitors from './visitors';\nimport * as validators from './validators';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\n\nimport { Model, Data } from '../types';\nimport { ValidationError } from '../errors';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\n\nconst { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants;\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, validateQuery throws when the query has any top-level key not in the allowed list.\n * Defaults to false for backward compatibility.\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Validator {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface ValidateFunc {\n (data: unknown, schema: Model, options?: Options): Promise<void>;\n}\n\nexport type ValidateQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<void>;\n\nexport type ValidateBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<void>;\n\ninterface APIOptions {\n validators?: Validators;\n getModel: (model: string) => Model;\n}\n\nexport interface Validators {\n input?: Validator[];\n}\n\nconst createAPIValidators = (opts: APIOptions) => {\n const { getModel } = opts || {};\n\n const validateInput: ValidateFunc = async (data: unknown, schema: Model, options = {}) => {\n const { auth, route } = options;\n if (!schema) {\n throw new Error('Missing schema in validateInput');\n }\n\n if (isArray(data)) {\n await Promise.all(data.map((entry) => validateInput(entry, schema, options)));\n return;\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n (data: unknown) => {\n if (isObject(data)) {\n if (ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: ID_ATTRIBUTE });\n }\n\n if (DOC_ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: DOC_ID_ATTRIBUTE });\n }\n }\n return data;\n },\n // non-writable attributes\n traverseEntity(visitors.throwRestrictedFields(nonWritableAttributes), { schema, getModel }),\n // unrecognized attributes (allowedExtraRootKeys = registered input param keys)\n traverseEntity(visitors.throwUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n }),\n ];\n\n if (auth) {\n // restricted relations\n transforms.push(\n traverseEntity(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n // Apply validators from registry if exists\n opts?.validators?.input?.forEach((validator: Validator) => transforms.push(validator(schema)));\n\n try {\n await pipeAsync(...transforms)(data as Data);\n\n // Validate extra root keys from route's body schema with Zod (throw on failure).\n //\n // Content-api sends the document payload as body.data; the controller calls validateInput(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 validated above by traverseEntity (throwUnrecognizedFields, 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 if (isObject(data) && route?.request?.body?.['application/json']) {\n const bodySchema = route.request.body['application/json'];\n if (typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n const dataObj = data as Record<string, unknown>;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in dataObj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).parse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(dataObj[key]);\n if (!result.success) {\n throw new ValidationError(\n (result.error?.message as string) ?? 'Validation failed',\n { key, path: null, source: 'body', param: key }\n );\n }\n }\n }\n }\n }\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'body';\n }\n throw e;\n }\n };\n\n const validateQuery = 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 validateQuery');\n }\n\n // Core allowlisted params are not covered by route.request.query Zod (see getExtraQueryKeysFromRoute).\n if ('publicationFilter' in query) {\n validatePublicationFilterQueryParam(query.publicationFilter);\n }\n\n if (strictParams) {\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n for (const key of Object.keys(query)) {\n if (!allowedKeys.includes(key)) {\n try {\n throwInvalidKey({ key, path: null });\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = key;\n }\n throw e;\n }\n }\n }\n // Validate extra query keys from route's request schema with Zod (throw on failure)\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).parse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (!result.success) {\n throw new ValidationError(\n (result.error?.message as string) ?? 'Invalid query param',\n { key, path: null, source: 'query', param: key }\n );\n }\n }\n }\n }\n }\n }\n\n const { filters, sort, fields, populate } = query;\n\n if (filters) {\n await validateFilters(filters, schema, { auth });\n }\n\n if (sort) {\n await validateSort(sort, schema, { auth });\n }\n\n if (fields) {\n await validateFields(fields, schema);\n }\n\n // a wildcard is always valid; its conversion will be handled by the entity service and can be optimized with sanitizer\n if (populate && populate !== '*') {\n await validatePopulate(populate, schema);\n }\n };\n\n const validateFilters: ValidateFunc = async (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateFilters');\n }\n if (isArray(filters)) {\n await Promise.all(filters.map((filter) => validateFilters(filter, schema, { auth })));\n return;\n }\n\n const transforms = [validators.defaultValidateFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(filters);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'filters';\n }\n throw e;\n }\n };\n\n const validateSort: ValidateFunc = async (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateSort');\n }\n const transforms = [validators.defaultValidateSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(sort);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'sort';\n }\n throw e;\n }\n };\n\n const validateFields: ValidateFunc = async (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in validateFields');\n }\n const transforms = [validators.defaultValidateFields({ schema, getModel })];\n\n try {\n await pipeAsync(...transforms)(fields);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'fields';\n }\n throw e;\n }\n };\n\n const validatePopulate: ValidateFunc = async (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [validators.defaultValidatePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(populate);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'populate';\n }\n throw e;\n }\n };\n\n return {\n input: validateInput,\n query: validateQuery,\n filters: validateFilters,\n sort: validateSort,\n fields: validateFields,\n populate: validatePopulate,\n };\n};\n\nexport { createAPIValidators, validators, visitors };\n\nexport type APIValidators = ReturnType<typeof createAPIValidators>;\n"],"names":["ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","constants","createAPIValidators","opts","getModel","validateInput","data","schema","options","auth","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","isObject","throwInvalidKey","key","traverseEntity","visitors","push","validators","input","forEach","validator","pipeAsync","request","body","bodySchema","shape","dataObj","Object","keys","zodSchema","parse","result","safeParse","success","ValidationError","error","message","path","source","param","e","details","validateQuery","query","strictParams","validatePublicationFilterQueryParam","publicationFilter","extraQueryKeys","getExtraQueryKeysFromRoute","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","includes","routeQuerySchema","filters","sort","fields","populate","validateFilters","validateSort","validateFields","validatePopulate","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../src/validate/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, isObject } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { getNonWritableAttributes, constants } 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';\nimport { throwInvalidKey } from './utils';\n\nimport * as visitors from './visitors';\nimport * as validators from './validators';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort } from '../traverse';\n\nimport { Model, Data } from '../types';\nimport { ValidationError } from '../errors';\nimport { validatePublicationFilterQueryParam } from '../publication-filter';\n\nconst { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants;\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, validateQuery throws when the query has any top-level key not in the allowed list.\n * Defaults to false for backward compatibility.\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Validator {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface ValidateFunc {\n (data: unknown, schema: Model, options?: Options): Promise<void>;\n}\n\nexport type ValidateQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<void>;\n\nexport type ValidateBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<void>;\n\ninterface APIOptions {\n validators?: Validators;\n getModel: (model: string) => Model;\n}\n\nexport interface Validators {\n input?: Validator[];\n}\n\nconst createAPIValidators = (opts: APIOptions) => {\n const { getModel } = opts || {};\n\n const validateInput: ValidateFunc = async (data: unknown, schema: Model, options = {}) => {\n const { auth, route } = options;\n if (!schema) {\n throw new Error('Missing schema in validateInput');\n }\n\n if (isArray(data)) {\n await Promise.all(data.map((entry) => validateInput(entry, schema, options)));\n return;\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n (data: unknown) => {\n if (isObject(data)) {\n if (ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: ID_ATTRIBUTE });\n }\n\n if (DOC_ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: DOC_ID_ATTRIBUTE });\n }\n }\n return data;\n },\n // non-writable attributes\n traverseEntity(visitors.throwRestrictedFields(nonWritableAttributes), { schema, getModel }),\n // unrecognized attributes (allowedExtraRootKeys = registered input param keys)\n traverseEntity(visitors.throwUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n }),\n ];\n\n if (auth) {\n // restricted relations\n transforms.push(\n traverseEntity(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n // Apply validators from registry if exists\n opts?.validators?.input?.forEach((validator: Validator) => transforms.push(validator(schema)));\n\n try {\n await pipeAsync(...transforms)(data as Data);\n\n // Validate extra root keys from route's body schema with Zod (throw on failure).\n //\n // Content-api sends the document payload as body.data; the controller calls validateInput(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 validated above by traverseEntity (throwUnrecognizedFields, 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 if (isObject(data) && route?.request?.body?.['application/json']) {\n const bodySchema = route.request.body['application/json'];\n if (typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n const dataObj = data as Record<string, unknown>;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in dataObj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).parse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(dataObj[key]);\n if (!result.success) {\n throw new ValidationError(\n (result.error?.message as string) ?? 'Validation failed',\n { key, path: null, source: 'body', param: key }\n );\n }\n }\n }\n }\n }\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'body';\n }\n throw e;\n }\n };\n\n const validateQuery = 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 validateQuery');\n }\n\n // Core allowlisted params are not covered by route.request.query Zod (see getExtraQueryKeysFromRoute).\n if ('publicationFilter' in query) {\n validatePublicationFilterQueryParam(query.publicationFilter);\n }\n\n if (strictParams) {\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n for (const key of Object.keys(query)) {\n if (!allowedKeys.includes(key)) {\n try {\n throwInvalidKey({ key, path: null });\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = key;\n }\n throw e;\n }\n }\n }\n // Validate extra query keys from route's request schema with Zod (throw on failure)\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).parse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (!result.success) {\n throw new ValidationError(\n (result.error?.message as string) ?? 'Invalid query param',\n { key, path: null, source: 'query', param: key }\n );\n }\n }\n }\n }\n }\n }\n\n const { filters, sort, fields, populate } = query;\n\n if (filters) {\n await validateFilters(filters, schema, { auth });\n }\n\n if (sort) {\n await validateSort(sort, schema, { auth });\n }\n\n if (fields) {\n await validateFields(fields, schema);\n }\n\n // a wildcard is always valid; its conversion will be handled by the entity service and can be optimized with sanitizer\n if (populate && populate !== '*') {\n await validatePopulate(populate, schema);\n }\n };\n\n const validateFilters: ValidateFunc = async (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateFilters');\n }\n if (isArray(filters)) {\n await Promise.all(filters.map((filter) => validateFilters(filter, schema, { auth })));\n return;\n }\n\n const transforms = [validators.defaultValidateFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(filters);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'filters';\n }\n throw e;\n }\n };\n\n const validateSort: ValidateFunc = async (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateSort');\n }\n const transforms = [validators.defaultValidateSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(sort);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'sort';\n }\n throw e;\n }\n };\n\n const validateFields: ValidateFunc = async (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in validateFields');\n }\n const transforms = [validators.defaultValidateFields({ schema, getModel })];\n\n try {\n await pipeAsync(...transforms)(fields);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'fields';\n }\n throw e;\n }\n };\n\n const validatePopulate: ValidateFunc = async (populate, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [validators.defaultValidatePopulate({ schema, getModel })];\n\n try {\n await pipeAsync(...transforms)(populate);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'populate';\n }\n throw e;\n }\n };\n\n return {\n input: validateInput,\n query: validateQuery,\n filters: validateFilters,\n sort: validateSort,\n fields: validateFields,\n populate: validatePopulate,\n };\n};\n\nexport { createAPIValidators, validators, visitors };\n\nexport type APIValidators = ReturnType<typeof createAPIValidators>;\n"],"names":["ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","constants","createAPIValidators","opts","getModel","validateInput","data","schema","options","auth","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","isObject","throwInvalidKey","key","traverseEntity","visitors","push","validators","input","forEach","validator","pipeAsync","request","body","bodySchema","shape","dataObj","Object","keys","zodSchema","parse","result","safeParse","success","ValidationError","error","message","path","source","param","e","details","validateQuery","query","strictParams","validatePublicationFilterQueryParam","publicationFilter","extraQueryKeys","getExtraQueryKeysFromRoute","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","includes","routeQuerySchema","filters","sort","fields","populate","validateFilters","validateSort","validateFields","validatePopulate","filter","traverseQueryFilters","traverseQuerySort"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throw-restricted-relations.d.ts","sourceRoot":"","sources":["../../../src/validate/visitors/throw-restricted-relations.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"throw-restricted-relations.d.ts","sourceRoot":"","sources":["../../../src/validate/visitors/throw-restricted-relations.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;yBAgBtC,MAAM,OAAO,KAAG,OAAO;AAAvC,wBAgII"}
|
|
@@ -19,7 +19,7 @@ var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, p
|
|
|
19
19
|
}
|
|
20
20
|
const handleMorphRelation = async ()=>{
|
|
21
21
|
const elements = data[key];
|
|
22
|
-
if (
|
|
22
|
+
if (isMorphMutationPayload(elements)) {
|
|
23
23
|
await handleMorphElements(elements.connect || []);
|
|
24
24
|
await handleMorphElements(elements.set || []);
|
|
25
25
|
await handleMorphElements(elements.disconnect || []);
|
|
@@ -37,13 +37,15 @@ var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, p
|
|
|
37
37
|
const optionKeys = Object.keys(elements.options);
|
|
38
38
|
// Validate each key based on its validator function
|
|
39
39
|
for (const key of optionKeys){
|
|
40
|
-
|
|
40
|
+
const optionKey = key;
|
|
41
|
+
const validator = relations.VALID_RELATION_ORDERING_KEYS[optionKey];
|
|
42
|
+
if (!validator) {
|
|
41
43
|
utils.throwInvalidKey({
|
|
42
44
|
key,
|
|
43
45
|
path: path.attribute
|
|
44
46
|
});
|
|
45
47
|
}
|
|
46
|
-
if (!
|
|
48
|
+
if (!validator(elements.options[key])) {
|
|
47
49
|
utils.throwInvalidKey({
|
|
48
50
|
key,
|
|
49
51
|
path: path.attribute
|
|
@@ -51,10 +53,30 @@ var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, p
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
}
|
|
56
|
+
} else if (isMorphPopulatePayload(elements)) {
|
|
57
|
+
for (const uid of Object.keys(elements.on)){
|
|
58
|
+
const scopes = ACTIONS_TO_VERIFY.map((action)=>`${uid}.${action}`);
|
|
59
|
+
const isAllowed = await hasAccessToSomeScopes(scopes, auth);
|
|
60
|
+
if (!isAllowed) {
|
|
61
|
+
utils.throwInvalidKey({
|
|
62
|
+
key,
|
|
63
|
+
path: path.attribute
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
54
67
|
} else {
|
|
55
68
|
await handleMorphElements(elements);
|
|
56
69
|
}
|
|
57
70
|
};
|
|
71
|
+
const isMorphMutationPayload = (value)=>{
|
|
72
|
+
return fp.isObject(value) && ('connect' in value || 'set' in value || 'disconnect' in value || 'options' in value);
|
|
73
|
+
};
|
|
74
|
+
const isMorphPopulatePayload = (value)=>{
|
|
75
|
+
return fp.isObject(value) && !('__type' in value) && 'on' in value && fp.isObject(value.on);
|
|
76
|
+
};
|
|
77
|
+
const isMorphPopulateAllOrCount = (value)=>{
|
|
78
|
+
return value === true || fp.isObject(value) && 'count' in value && value.count === true;
|
|
79
|
+
};
|
|
58
80
|
const handleMorphElements = async (elements)=>{
|
|
59
81
|
if (!fp.isArray(elements)) {
|
|
60
82
|
utils.throwInvalidKey({
|
|
@@ -62,14 +84,16 @@ var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, p
|
|
|
62
84
|
path: path.attribute
|
|
63
85
|
});
|
|
64
86
|
}
|
|
65
|
-
|
|
66
|
-
|
|
87
|
+
const morphElements = elements;
|
|
88
|
+
for (const element of morphElements){
|
|
89
|
+
if (!fp.isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {
|
|
67
90
|
utils.throwInvalidKey({
|
|
68
91
|
key,
|
|
69
92
|
path: path.attribute
|
|
70
93
|
});
|
|
71
94
|
}
|
|
72
|
-
const
|
|
95
|
+
const type = element.__type;
|
|
96
|
+
const scopes = ACTIONS_TO_VERIFY.map((action)=>`${type}.${action}`);
|
|
73
97
|
const isAllowed = await hasAccessToSomeScopes(scopes, auth);
|
|
74
98
|
if (!isAllowed) {
|
|
75
99
|
utils.throwInvalidKey({
|
|
@@ -96,6 +120,10 @@ var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, p
|
|
|
96
120
|
].includes(key);
|
|
97
121
|
// Polymorphic relations
|
|
98
122
|
if (contentTypes.isMorphToRelationalAttribute(attribute)) {
|
|
123
|
+
const value = data[key];
|
|
124
|
+
if (isMorphPopulateAllOrCount(value)) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
99
127
|
await handleMorphRelation();
|
|
100
128
|
return;
|
|
101
129
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throw-restricted-relations.js","sources":["../../../src/validate/visitors/throw-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport { throwInvalidKey } from '../utils';\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 }>;\n\nexport default (auth: unknown): Visitor =>\n async ({ data, key, attribute, schema, path }) => {\n if (!attribute) {\n return;\n }\n\n const isRelation = attribute.type === 'relation';\n\n if (!isRelation) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements: any = (data as Record<string, MorphArray>)[key];\n\n if (\n 'connect' in elements ||\n 'set' in elements ||\n 'disconnect' in elements ||\n 'options' in elements\n ) {\n await handleMorphElements(elements.connect || []);\n await handleMorphElements(elements.set || []);\n await handleMorphElements(elements.disconnect || []);\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 ('options' in elements) {\n if (elements.options === null || elements.options === undefined) {\n return;\n }\n\n if (typeof elements.options !== 'object') {\n throwInvalidKey({ key, path: path.attribute });\n }\n\n const optionKeys = Object.keys(elements.options);\n\n // Validate each key based on its validator function\n for (const key of optionKeys) {\n if (!(key in VALID_RELATION_ORDERING_KEYS)) {\n throwInvalidKey({ key, path: path.attribute });\n }\n if (!VALID_RELATION_ORDERING_KEYS[key](elements.options[key])) {\n throwInvalidKey({ key, path: path.attribute });\n }\n }\n }\n } else {\n await handleMorphElements(elements);\n }\n };\n\n const handleMorphElements = async (elements: any[]) => {\n if (!isArray(elements)) {\n throwInvalidKey({ key, path: path.attribute });\n }\n\n for (const element of elements) {\n if (!isObject(element) || !('__type' in element)) {\n throwInvalidKey({ key, path: path.attribute });\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 throwInvalidKey({ key, path: path.attribute });\n }\n }\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\n if (!isAllowed) {\n throwInvalidKey({ key, path: path.attribute });\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\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n try {\n await strapi.auth.verify(auth, { scope });\n return true;\n } catch {\n continue;\n }\n }\n\n return false;\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","data","key","attribute","schema","path","isRelation","type","handleMorphRelation","elements","handleMorphElements","connect","set","disconnect","options","undefined","throwInvalidKey","optionKeys","Object","keys","VALID_RELATION_ORDERING_KEYS","isArray","element","isObject","scopes","map","action","__type","isAllowed","hasAccessToSomeScopes","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scope","strapi","verify"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"throw-restricted-relations.js","sources":["../../../src/validate/visitors/throw-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport { throwInvalidKey } from '../utils';\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 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};\n\nexport default (auth: unknown): Visitor =>\n async ({ data, key, attribute, schema, path }) => {\n if (!attribute) {\n return;\n }\n\n const isRelation = attribute.type === 'relation';\n\n if (!isRelation) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements = (data as Record<string, unknown>)[key];\n\n if (isMorphMutationPayload(elements)) {\n await handleMorphElements(elements.connect || []);\n await handleMorphElements(elements.set || []);\n await handleMorphElements(elements.disconnect || []);\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 ('options' in elements) {\n if (elements.options === null || elements.options === undefined) {\n return;\n }\n\n if (typeof elements.options !== 'object') {\n throwInvalidKey({ key, path: path.attribute });\n }\n\n const optionKeys = Object.keys(elements.options);\n\n // Validate each key based on its validator function\n for (const key of optionKeys) {\n const optionKey = key as keyof typeof VALID_RELATION_ORDERING_KEYS;\n const validator = VALID_RELATION_ORDERING_KEYS[optionKey];\n\n if (!validator) {\n throwInvalidKey({ key, path: path.attribute });\n }\n if (!validator(elements.options[key])) {\n throwInvalidKey({ key, path: path.attribute });\n }\n }\n }\n } else if (isMorphPopulatePayload(elements)) {\n for (const uid of Object.keys(elements.on)) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (!isAllowed) {\n throwInvalidKey({ key, path: path.attribute });\n }\n }\n } else {\n await handleMorphElements(elements);\n }\n };\n\n const isMorphMutationPayload = (value: unknown): value is MorphMutationPayload => {\n return (\n isObject(value) &&\n ('connect' in value || 'set' in value || 'disconnect' in value || 'options' in value)\n );\n };\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 isMorphPopulateAllOrCount = (value: unknown): value is true | { count: true } => {\n return value === true || (isObject(value) && 'count' in value && value.count === true);\n };\n\n const handleMorphElements = async (elements: unknown) => {\n if (!isArray(elements)) {\n throwInvalidKey({ key, path: path.attribute });\n }\n\n const morphElements = elements as unknown[];\n\n for (const element of morphElements) {\n if (!isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {\n throwInvalidKey({ key, path: path.attribute });\n }\n\n const type = (element as { __type: string }).__type;\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (!isAllowed) {\n throwInvalidKey({ key, path: path.attribute });\n }\n }\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\n if (!isAllowed) {\n throwInvalidKey({ key, path: path.attribute });\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n // Polymorphic relations\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n const value = (data as Record<string, unknown>)[key];\n if (isMorphPopulateAllOrCount(value)) {\n return;\n }\n\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\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n try {\n await strapi.auth.verify(auth, { scope });\n return true;\n } catch {\n continue;\n }\n }\n\n return false;\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","data","key","attribute","schema","path","isRelation","type","handleMorphRelation","elements","isMorphMutationPayload","handleMorphElements","connect","set","disconnect","options","undefined","throwInvalidKey","optionKeys","Object","keys","optionKey","validator","VALID_RELATION_ORDERING_KEYS","isMorphPopulatePayload","uid","on","scopes","map","action","isAllowed","hasAccessToSomeScopes","value","isObject","isMorphPopulateAllOrCount","count","isArray","morphElements","element","__type","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scope","strapi","verify"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isObject, isArray } from 'lodash/fp';
|
|
2
2
|
import { isMorphToRelationalAttribute, constants } from '../../content-types.mjs';
|
|
3
3
|
import { throwInvalidKey } from '../utils.mjs';
|
|
4
4
|
import { VALID_RELATION_ORDERING_KEYS } from '../../relations.mjs';
|
|
@@ -17,7 +17,7 @@ var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, p
|
|
|
17
17
|
}
|
|
18
18
|
const handleMorphRelation = async ()=>{
|
|
19
19
|
const elements = data[key];
|
|
20
|
-
if (
|
|
20
|
+
if (isMorphMutationPayload(elements)) {
|
|
21
21
|
await handleMorphElements(elements.connect || []);
|
|
22
22
|
await handleMorphElements(elements.set || []);
|
|
23
23
|
await handleMorphElements(elements.disconnect || []);
|
|
@@ -35,13 +35,15 @@ var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, p
|
|
|
35
35
|
const optionKeys = Object.keys(elements.options);
|
|
36
36
|
// Validate each key based on its validator function
|
|
37
37
|
for (const key of optionKeys){
|
|
38
|
-
|
|
38
|
+
const optionKey = key;
|
|
39
|
+
const validator = VALID_RELATION_ORDERING_KEYS[optionKey];
|
|
40
|
+
if (!validator) {
|
|
39
41
|
throwInvalidKey({
|
|
40
42
|
key,
|
|
41
43
|
path: path.attribute
|
|
42
44
|
});
|
|
43
45
|
}
|
|
44
|
-
if (!
|
|
46
|
+
if (!validator(elements.options[key])) {
|
|
45
47
|
throwInvalidKey({
|
|
46
48
|
key,
|
|
47
49
|
path: path.attribute
|
|
@@ -49,10 +51,30 @@ var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, p
|
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
}
|
|
54
|
+
} else if (isMorphPopulatePayload(elements)) {
|
|
55
|
+
for (const uid of Object.keys(elements.on)){
|
|
56
|
+
const scopes = ACTIONS_TO_VERIFY.map((action)=>`${uid}.${action}`);
|
|
57
|
+
const isAllowed = await hasAccessToSomeScopes(scopes, auth);
|
|
58
|
+
if (!isAllowed) {
|
|
59
|
+
throwInvalidKey({
|
|
60
|
+
key,
|
|
61
|
+
path: path.attribute
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
52
65
|
} else {
|
|
53
66
|
await handleMorphElements(elements);
|
|
54
67
|
}
|
|
55
68
|
};
|
|
69
|
+
const isMorphMutationPayload = (value)=>{
|
|
70
|
+
return isObject(value) && ('connect' in value || 'set' in value || 'disconnect' in value || 'options' in value);
|
|
71
|
+
};
|
|
72
|
+
const isMorphPopulatePayload = (value)=>{
|
|
73
|
+
return isObject(value) && !('__type' in value) && 'on' in value && isObject(value.on);
|
|
74
|
+
};
|
|
75
|
+
const isMorphPopulateAllOrCount = (value)=>{
|
|
76
|
+
return value === true || isObject(value) && 'count' in value && value.count === true;
|
|
77
|
+
};
|
|
56
78
|
const handleMorphElements = async (elements)=>{
|
|
57
79
|
if (!isArray(elements)) {
|
|
58
80
|
throwInvalidKey({
|
|
@@ -60,14 +82,16 @@ var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, p
|
|
|
60
82
|
path: path.attribute
|
|
61
83
|
});
|
|
62
84
|
}
|
|
63
|
-
|
|
64
|
-
|
|
85
|
+
const morphElements = elements;
|
|
86
|
+
for (const element of morphElements){
|
|
87
|
+
if (!isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {
|
|
65
88
|
throwInvalidKey({
|
|
66
89
|
key,
|
|
67
90
|
path: path.attribute
|
|
68
91
|
});
|
|
69
92
|
}
|
|
70
|
-
const
|
|
93
|
+
const type = element.__type;
|
|
94
|
+
const scopes = ACTIONS_TO_VERIFY.map((action)=>`${type}.${action}`);
|
|
71
95
|
const isAllowed = await hasAccessToSomeScopes(scopes, auth);
|
|
72
96
|
if (!isAllowed) {
|
|
73
97
|
throwInvalidKey({
|
|
@@ -94,6 +118,10 @@ var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, p
|
|
|
94
118
|
].includes(key);
|
|
95
119
|
// Polymorphic relations
|
|
96
120
|
if (isMorphToRelationalAttribute(attribute)) {
|
|
121
|
+
const value = data[key];
|
|
122
|
+
if (isMorphPopulateAllOrCount(value)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
97
125
|
await handleMorphRelation();
|
|
98
126
|
return;
|
|
99
127
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throw-restricted-relations.mjs","sources":["../../../src/validate/visitors/throw-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport { throwInvalidKey } from '../utils';\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 }>;\n\nexport default (auth: unknown): Visitor =>\n async ({ data, key, attribute, schema, path }) => {\n if (!attribute) {\n return;\n }\n\n const isRelation = attribute.type === 'relation';\n\n if (!isRelation) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements: any = (data as Record<string, MorphArray>)[key];\n\n if (\n 'connect' in elements ||\n 'set' in elements ||\n 'disconnect' in elements ||\n 'options' in elements\n ) {\n await handleMorphElements(elements.connect || []);\n await handleMorphElements(elements.set || []);\n await handleMorphElements(elements.disconnect || []);\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 ('options' in elements) {\n if (elements.options === null || elements.options === undefined) {\n return;\n }\n\n if (typeof elements.options !== 'object') {\n throwInvalidKey({ key, path: path.attribute });\n }\n\n const optionKeys = Object.keys(elements.options);\n\n // Validate each key based on its validator function\n for (const key of optionKeys) {\n if (!(key in VALID_RELATION_ORDERING_KEYS)) {\n throwInvalidKey({ key, path: path.attribute });\n }\n if (!VALID_RELATION_ORDERING_KEYS[key](elements.options[key])) {\n throwInvalidKey({ key, path: path.attribute });\n }\n }\n }\n } else {\n await handleMorphElements(elements);\n }\n };\n\n const handleMorphElements = async (elements: any[]) => {\n if (!isArray(elements)) {\n throwInvalidKey({ key, path: path.attribute });\n }\n\n for (const element of elements) {\n if (!isObject(element) || !('__type' in element)) {\n throwInvalidKey({ key, path: path.attribute });\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 throwInvalidKey({ key, path: path.attribute });\n }\n }\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\n if (!isAllowed) {\n throwInvalidKey({ key, path: path.attribute });\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\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n try {\n await strapi.auth.verify(auth, { scope });\n return true;\n } catch {\n continue;\n }\n }\n\n return false;\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","data","key","attribute","schema","path","isRelation","type","handleMorphRelation","elements","handleMorphElements","connect","set","disconnect","options","undefined","throwInvalidKey","optionKeys","Object","keys","VALID_RELATION_ORDERING_KEYS","isArray","element","isObject","scopes","map","action","__type","isAllowed","hasAccessToSomeScopes","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scope","strapi","verify"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"throw-restricted-relations.mjs","sources":["../../../src/validate/visitors/throw-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport { throwInvalidKey } from '../utils';\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 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};\n\nexport default (auth: unknown): Visitor =>\n async ({ data, key, attribute, schema, path }) => {\n if (!attribute) {\n return;\n }\n\n const isRelation = attribute.type === 'relation';\n\n if (!isRelation) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements = (data as Record<string, unknown>)[key];\n\n if (isMorphMutationPayload(elements)) {\n await handleMorphElements(elements.connect || []);\n await handleMorphElements(elements.set || []);\n await handleMorphElements(elements.disconnect || []);\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 ('options' in elements) {\n if (elements.options === null || elements.options === undefined) {\n return;\n }\n\n if (typeof elements.options !== 'object') {\n throwInvalidKey({ key, path: path.attribute });\n }\n\n const optionKeys = Object.keys(elements.options);\n\n // Validate each key based on its validator function\n for (const key of optionKeys) {\n const optionKey = key as keyof typeof VALID_RELATION_ORDERING_KEYS;\n const validator = VALID_RELATION_ORDERING_KEYS[optionKey];\n\n if (!validator) {\n throwInvalidKey({ key, path: path.attribute });\n }\n if (!validator(elements.options[key])) {\n throwInvalidKey({ key, path: path.attribute });\n }\n }\n }\n } else if (isMorphPopulatePayload(elements)) {\n for (const uid of Object.keys(elements.on)) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (!isAllowed) {\n throwInvalidKey({ key, path: path.attribute });\n }\n }\n } else {\n await handleMorphElements(elements);\n }\n };\n\n const isMorphMutationPayload = (value: unknown): value is MorphMutationPayload => {\n return (\n isObject(value) &&\n ('connect' in value || 'set' in value || 'disconnect' in value || 'options' in value)\n );\n };\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 isMorphPopulateAllOrCount = (value: unknown): value is true | { count: true } => {\n return value === true || (isObject(value) && 'count' in value && value.count === true);\n };\n\n const handleMorphElements = async (elements: unknown) => {\n if (!isArray(elements)) {\n throwInvalidKey({ key, path: path.attribute });\n }\n\n const morphElements = elements as unknown[];\n\n for (const element of morphElements) {\n if (!isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {\n throwInvalidKey({ key, path: path.attribute });\n }\n\n const type = (element as { __type: string }).__type;\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (!isAllowed) {\n throwInvalidKey({ key, path: path.attribute });\n }\n }\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\n if (!isAllowed) {\n throwInvalidKey({ key, path: path.attribute });\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n // Polymorphic relations\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n const value = (data as Record<string, unknown>)[key];\n if (isMorphPopulateAllOrCount(value)) {\n return;\n }\n\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\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n try {\n await strapi.auth.verify(auth, { scope });\n return true;\n } catch {\n continue;\n }\n }\n\n return false;\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","data","key","attribute","schema","path","isRelation","type","handleMorphRelation","elements","isMorphMutationPayload","handleMorphElements","connect","set","disconnect","options","undefined","throwInvalidKey","optionKeys","Object","keys","optionKey","validator","VALID_RELATION_ORDERING_KEYS","isMorphPopulatePayload","uid","on","scopes","map","action","isAllowed","hasAccessToSomeScopes","value","isObject","isMorphPopulateAllOrCount","count","isArray","morphElements","element","__type","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scope","strapi","verify"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/utils",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.53.0",
|
|
4
4
|
"description": "Shared utilities for the Strapi packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"strapi",
|
|
@@ -65,13 +65,13 @@
|
|
|
65
65
|
"@types/jest": "29.5.2",
|
|
66
66
|
"@types/koa": "2.15.2",
|
|
67
67
|
"@types/node": "20.19.41",
|
|
68
|
-
"eslint-config-custom": "5.
|
|
68
|
+
"eslint-config-custom": "5.53.0",
|
|
69
69
|
"jest": "29.6.0",
|
|
70
70
|
"koa": "2.16.4",
|
|
71
71
|
"koa-body": "6.0.1",
|
|
72
|
-
"tsconfig": "5.
|
|
72
|
+
"tsconfig": "5.53.0",
|
|
73
73
|
"vitest": "catalog:",
|
|
74
|
-
"vitest-config": "5.
|
|
74
|
+
"vitest-config": "5.53.0"
|
|
75
75
|
},
|
|
76
76
|
"engines": {
|
|
77
77
|
"node": ">=20.0.0 <=26.x.x",
|