{"record":{"id":"0d1b65207986a6c0","repo":"zylon-ai/private-gpt","slug":"allof-must-be-an-array-of-schemas","errorCode":null,"errorMessage":"'allOf' must be an array of schemas","messagePattern":"'allOf' must be an array of schemas","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"private_gpt/chat/schema_models.py","lineNumber":109,"sourceCode":"\n    # Handle combinators first\n    if \"oneOf\" in schema:\n        if not isinstance(schema[\"oneOf\"], list):\n            raise ValueError(\"'oneOf' must be an array of schemas\")\n        for sub_schema in schema[\"oneOf\"]:\n            _validate_json_schema_item(sub_schema, strict=False)\n        return  # oneOf schemas don't need type field\n\n    if \"anyOf\" in schema:\n        if not isinstance(schema[\"anyOf\"], list):\n            raise ValueError(\"'anyOf' must be an array of schemas\")\n        for sub_schema in schema[\"anyOf\"]:\n            _validate_json_schema_item(sub_schema, strict=False)\n        return  # anyOf schemas don't need type field\n\n    if \"allOf\" in schema:\n        if not isinstance(schema[\"allOf\"], list):\n            raise ValueError(\"'allOf' must be an array of schemas\")\n        for sub_schema in schema[\"allOf\"]:\n            _validate_json_schema_item(sub_schema, strict=False)\n        return  # allOf schemas don't need type field\n\n    # Regular schema validation\n    if \"type\" not in schema:\n        if strict:\n            raise ValueError(\"Schema must define a 'type' field\")\n        else:\n            return  # Non-strict mode allows missing type\n\n    if schema[\"type\"] == \"array\" and \"items\" not in schema:\n        raise ValueError(\"Array schemas must define 'items'\")\n\n    if schema[\"type\"] == \"array\" and not isinstance(schema.get(\"items\"), dict):\n        raise ValueError(\"Array 'items' must be a dictionary representing JSON Schema\")\n\n    # Recursively validate array items","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/chat/schema_models.py#L91-L127","documentation":"Pydantic field-validator error on SkillFile.path when any '/'-separated segment equals '..' — the classic traversal pattern. It prevents skill file entries from escaping the skill root via relative traversal; FastAPI surfaces it as 422.","triggerScenarios":"Submitting path 'assets/../../SKILL.md' or any segment equal to '..'; naive concatenation of user input into the path field; security probing of the skills upload API.","commonSituations":"Client code joining a base directory with untrusted relative input; porting archives containing '..' entries; penetration tests expecting traversal to be blocked.","solutions":["Sanitize: split on '/', drop '', '.' and '..' segments, rejoin with '/'.","Reject input containing '..' before it reaches the API call.","Use ids/names produced by your own listing flow instead of raw user strings."],"exampleFix":"# before\npath = f\"{user_dir}/{user_input}\"  # user_input = '../../etc/passwd'\n\n# after\nparts = [s for s in f\"{user_dir}/{user_input}\".split('/') if s not in ('', '.', '..')]\nif '..' in f\"{user_dir}/{user_input}\".split('/'):\n    raise ValueError('invalid path')\npath = '/'.join(parts)","handlingStrategy":"validation","validationCode":"function sanitizeSkillPath(raw) {\n  if (raw.split('/').includes('..')) throw new Error('traversal rejected');\n  return raw.split('/').filter((s) => s && s !== '.').join('/');\n}","typeGuard":"const hasNoDotDot = (p) => typeof p === 'string' && !p.split('/').includes('..');","tryCatchPattern":null,"preventionTips":["Reject '..' segments before they reach the API","Never concatenate untrusted input into skill file paths","Add security tests asserting traversal payloads fail client-side validation"],"tags":["pydantic","http-422","validation","skills","path-traversal","security"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}