{"record":{"id":"7d62600a3c780839","repo":"ArchiveBox/ArchiveBox","slug":"error-message-from-validate-persona-name","errorCode":null,"errorMessage":"error_message (from validate_persona_name)","messagePattern":"error_message \\(from validate_persona_name\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"archivebox/api/v1_personas.py","lineNumber":136,"sourceCode":"@paginate(CustomPagination)\ndef get_personas(request: HttpRequest):\n    \"\"\"List personas available on this ArchiveBox server.\"\"\"\n    return Persona.objects.all().order_by(\"name\")\n\n\n@router.post(\"/sync\", response=PersonaSyncResponseSchema, url_name=\"sync_persona\")\ndef sync_persona(request: HttpRequest, payload: PersonaSyncSchema):\n    \"\"\"\n    Create or update a Persona from a browser extension profile export.\n\n    The extension sends browser settings plus portable auth artifacts. The server\n    keeps browser override settings in Persona.config and writes cookies.txt /\n    auth.json into the persona directory for extractors to consume.\n    \"\"\"\n    name = payload.name.strip()\n    is_valid, error_message = validate_persona_name(name)\n    if not is_valid:\n        raise ValueError(error_message)\n\n    persona = find_persona(payload.extension_persona_id, name)\n    created = persona is None\n    if persona is None:\n        persona = Persona(name=name)\n        if request.user.is_authenticated:\n            persona.created_by = request.user\n\n    persona.config = {\n        **(persona.config or {}),\n        **browser_settings_to_config(payload.extension_persona_id, payload.settings),\n    }\n    persona.save()\n    persona.ensure_dirs()\n\n    cookies_written = False\n    if payload.cookies_txt.strip():\n        (persona.path / \"cookies.txt\").write_text(payload.cookies_txt)","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/ArchiveBox/ArchiveBox/blob/74564b28220090664f919479e82cbf454125fa34/archivebox/api/v1_personas.py#L118-L154","documentation":"sync_persona (POST /api/v1/persona/sync) strips the requested persona name and validates it with validate_persona_name, raising ValueError(error_message) on failure. The validator rejects empty names, path separators (/ or \\), parent-directory references (..), leading dots, and null bytes/newlines, because persona names become directory names under PERSONAS_DIR and must not enable path traversal.","triggerScenarios":"POST /api/v1/persona/sync with payload.name that is: empty/whitespace, contains '/' or '\\\\', contains '..', starts with '.', or contains \\x00 / \\n / \\r characters.","commonSituations":"Deriving a persona name from a URL or file path without sanitizing it (e.g. 'profiles/alice' or '../default'); users entering names with leading dots like '.chrome-default'; passing empty name fields from form submissions.","solutions":["Sanitize the persona name: strip whitespace, remove/replace path separators, leading dots, and '..' before calling the API","Use a simple slug of the name (letters, digits, dashes, underscores) e.g. 'work-profile' instead of 'work/profile'","Pre-validate with the same rules as validate_persona_name (archivebox/cli/archivebox_persona.py:159) client-side to get a friendlier error"],"exampleFix":"// before\n{\"name\": \"profiles/work\"}\n// after\n{\"name\": \"profiles-work\"}","handlingStrategy":"validation","validationCode":"function validatePersonaName(name) {\n  const n = name.trim();\n  if (!n) return 'Persona name cannot be empty';\n  if (/[\\/\\\\]/.test(n)) return 'no path separators';\n  if (n.includes('..')) return 'no parent references';\n  if (n.startsWith('.')) return 'no leading dot';\n  if (/[\\x00\\n\\r]/.test(n)) return 'invalid characters';\n  return null;\n}","typeGuard":"const isValidPersonaName = (name) => {\n  const n = name.trim();\n  return n.length > 0 && !/[\\/\\\\]/.test(n) && !n.includes('..') && !n.startsWith('.') && !/[\\x00\\n\\r]/.test(n);\n};","tryCatchPattern":"try {\n  await syncPersona({ name });\n} catch (e) {\n  if (e instanceof ValueError || /Persona name/i.test(e.message)) {\n    // sanitize: slugify the name and retry once\n    return syncPersona({ name: slugify(name) });\n  }\n  throw e;\n}","preventionTips":["Slugify names derived from URLs/paths before calling the API (keep [a-zA-Z0-9-_])","Trim whitespace and reject empty names in the form/UI layer","Never build persona names by joining path segments; treat the name as a single directory label"],"tags":["api","validation","path-traversal","personas"],"backgroundTag":"invalid-persona-name","analyzedSha":"74564b28220090664f919479e82cbf454125fa34","analyzedAt":"2026-08-28T23:56:51.556Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}