{"version":3,"sources":["../../../src/database/queryValidation/validateSearchParams.ts"],"sourcesContent":["import type { SanitizedCollectionConfig } from '../../collections/config/types.js'\nimport type { FlattenedField } from '../../fields/config/types.js'\nimport type { SanitizedGlobalConfig } from '../../globals/config/types.js'\nimport type { PayloadRequest, WhereField } from '../../types/index.js'\nimport type { EntityPolicies, PathToQuery } from './types.js'\n\nimport { fieldAffectsData } from '../../fields/config/types.js'\nimport { SAFE_FIELD_PATH_REGEX } from '../../types/constants.js'\nimport { getEntityPermissions } from '../../utilities/getEntityPermissions/getEntityPermissions.js'\nimport { isolateObjectProperty } from '../../utilities/isolateObjectProperty.js'\nimport { getLocalizedPaths } from '../getLocalizedPaths.js'\nimport { validateQueryPaths } from './validateQueryPaths.js'\n\ntype Args = {\n  collectionConfig?: SanitizedCollectionConfig\n  constraint: WhereField\n  errors: { path: string }[]\n  fields: FlattenedField[]\n  globalConfig?: SanitizedGlobalConfig\n  operator: string\n  overrideAccess: boolean\n  parentIsLocalized?: boolean\n  path: string\n  // TODO: Rename to permissions or entityPermissions in 4.0\n  policies: EntityPolicies\n  polymorphicJoin?: boolean\n  req: PayloadRequest\n  val: unknown\n  versionFields?: FlattenedField[]\n}\n\n/**\n * Validate the Payload key / value / operator\n */\nexport async function validateSearchParam({\n  collectionConfig,\n  constraint,\n  errors,\n  fields,\n  globalConfig,\n  operator,\n  overrideAccess,\n  parentIsLocalized,\n  path: incomingPath,\n  policies,\n  polymorphicJoin,\n  req,\n  val,\n  versionFields,\n}: Args): Promise<void> {\n  // Replace GraphQL nested field double underscore formatting\n  let sanitizedPath\n  if (incomingPath === '_id') {\n    sanitizedPath = 'id'\n  } else {\n    sanitizedPath = incomingPath.replace(/__/g, '.')\n  }\n  let paths: PathToQuery[] = []\n  const { slug } = (collectionConfig || globalConfig)!\n\n  const blockReferencesPermissions = {}\n\n  if (globalConfig && !policies.globals![slug]) {\n    policies.globals![slug] = await getEntityPermissions({\n      blockReferencesPermissions,\n      entity: globalConfig,\n      entityType: 'global',\n      fetchData: false,\n      operations: ['read'],\n      req,\n    })\n  }\n\n  if (sanitizedPath !== 'id') {\n    paths = getLocalizedPaths({\n      collectionSlug: collectionConfig?.slug,\n      fields,\n      globalSlug: globalConfig?.slug,\n      incomingPath: sanitizedPath,\n      locale: req.locale!,\n      overrideAccess,\n      parentIsLocalized,\n      payload: req.payload,\n    })\n  }\n  const promises: Promise<void>[] = []\n\n  // Sanitize relation.otherRelation.id to relation.otherRelation\n  if (paths.at(-1)?.path === 'id') {\n    const previousField = paths.at(-2)?.field\n    if (\n      previousField &&\n      (previousField.type === 'relationship' || previousField.type === 'upload') &&\n      typeof previousField.relationTo === 'string'\n    ) {\n      paths.pop()\n    }\n  }\n\n  promises.push(\n    ...paths.map(async ({ collectionSlug, field, invalid, path }, i) => {\n      if (invalid) {\n        if (!polymorphicJoin || !SAFE_FIELD_PATH_REGEX.test(incomingPath)) {\n          errors.push({ path })\n        }\n\n        return\n      }\n\n      // where: { relatedPosts: { equals: 1}} -> { 'relatedPosts.id': { equals: 1}}\n      if (field.type === 'join' && path === incomingPath) {\n        constraint[`${path}.id` as keyof WhereField] = constraint[path as keyof WhereField]\n        delete constraint[path as keyof WhereField]\n      }\n\n      if ('virtual' in field && field.virtual) {\n        if (field.virtual === true) {\n          errors.push({ path })\n        }\n      }\n\n      if (polymorphicJoin && path === 'relationTo') {\n        return\n      }\n\n      if (!overrideAccess && fieldAffectsData(field)) {\n        if (collectionSlug) {\n          if (!policies.collections![collectionSlug]) {\n            policies.collections![collectionSlug] = await getEntityPermissions({\n              blockReferencesPermissions,\n              entity: req.payload.collections[collectionSlug]!.config,\n              entityType: 'collection',\n              fetchData: false,\n              operations: ['read'],\n              req: isolateObjectProperty(req, 'transactionID'),\n            })\n          }\n\n          if (\n            ['hash', 'salt'].includes(incomingPath) &&\n            collectionConfig!.auth &&\n            !collectionConfig!.auth?.disableLocalStrategy\n          ) {\n            errors.push({ path: incomingPath })\n          }\n        }\n        let fieldPath = path\n        // remove locale from end of path\n        if (path.endsWith(`.${req.locale}`)) {\n          fieldPath = path.slice(0, -(req.locale!.length + 1))\n        }\n        // remove \".value\" from ends of polymorphic relationship paths\n        if (\n          (field.type === 'relationship' || field.type === 'upload') &&\n          Array.isArray(field.relationTo)\n        ) {\n          fieldPath = fieldPath.replace('.value', '')\n        }\n\n        const entityType: 'collections' | 'globals' = globalConfig ? 'globals' : 'collections'\n        const entitySlug = collectionSlug || globalConfig!.slug\n        const segments = fieldPath.split('.')\n\n        let fieldAccess: any\n\n        if (versionFields) {\n          fieldAccess = policies[entityType]![entitySlug]!.fields\n\n          if (\n            segments[0] === 'parent' ||\n            segments[0] === 'version' ||\n            segments[0] === 'snapshot' ||\n            segments[0] === 'latest'\n          ) {\n            segments.shift()\n          }\n        } else {\n          fieldAccess = policies[entityType]![entitySlug]!.fields\n        }\n\n        if (segments.length) {\n          segments.forEach((segment) => {\n            if (fieldAccess[segment]) {\n              if ('fields' in fieldAccess[segment]) {\n                fieldAccess = fieldAccess[segment].fields\n              } else {\n                fieldAccess = fieldAccess[segment]\n              }\n            }\n          })\n\n          if (!fieldAccess?.read?.permission) {\n            errors.push({ path: fieldPath })\n          }\n        }\n      }\n\n      if (i > 1) {\n        // Remove top collection and reverse array\n        // to work backwards from top\n        const pathsToQuery = paths.slice(1).reverse()\n\n        pathsToQuery.forEach(\n          ({ collectionSlug: pathCollectionSlug, path: subPath }, pathToQueryIndex) => {\n            // On the \"deepest\" collection,\n            // validate query of the relationship\n            if (pathToQueryIndex === 0) {\n              promises.push(\n                validateQueryPaths({\n                  collectionConfig: req.payload.collections[pathCollectionSlug!]!.config,\n                  errors,\n                  globalConfig: undefined,\n                  overrideAccess,\n                  policies,\n                  req,\n                  where: {\n                    [subPath]: {\n                      [operator]: val,\n                    },\n                  },\n                }),\n              )\n            }\n          },\n        )\n      }\n    }),\n  )\n  await Promise.all(promises)\n}\n"],"names":["fieldAffectsData","SAFE_FIELD_PATH_REGEX","getEntityPermissions","isolateObjectProperty","getLocalizedPaths","validateQueryPaths","validateSearchParam","collectionConfig","constraint","errors","fields","globalConfig","operator","overrideAccess","parentIsLocalized","path","incomingPath","policies","polymorphicJoin","req","val","versionFields","sanitizedPath","replace","paths","slug","blockReferencesPermissions","globals","entity","entityType","fetchData","operations","collectionSlug","globalSlug","locale","payload","promises","at","previousField","field","type","relationTo","pop","push","map","invalid","i","test","virtual","collections","config","includes","auth","disableLocalStrategy","fieldPath","endsWith","slice","length","Array","isArray","entitySlug","segments","split","fieldAccess","shift","forEach","segment","read","permission","pathsToQuery","reverse","pathCollectionSlug","subPath","pathToQueryIndex","undefined","where","Promise","all"],"mappings":"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"}