{"record":{"id":"2bc674c77024b675","repo":"unicity-aos/aos-ce","slug":"product-manifest-path-must-be-a-regular-file","errorCode":null,"errorMessage":"product manifest path must be a regular file","messagePattern":"product manifest path must be a regular file","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/unicity-aos-bootstrap/src/lib.rs","lineNumber":197,"sourceCode":"        };\n        validate_capsule_dir(&path, &capsule_assets_from_manifest()?)\n    }\n\n    /// Materialize the Unicity CE manifest embedded in this product binary.\n    ///\n    /// The product CLI hands this local path to the neutral runtime, so first-run\n    /// provisioning uses the manifest shipped with the installed AOS release rather\n    /// than following a mutable repository branch.\n    ///\n    /// # Errors\n    /// Returns an error when the product manifest cannot be written atomically.\n    pub fn ensure_unicity_ce_manifest(&self) -> io::Result<PathBuf> {\n        let path = self.unicity_ce_manifest_path();\n        let capsule_dir = self.capsule_dir()?;\n        let manifest = materialize_manifest(&capsule_dir)?;\n        match fs::symlink_metadata(&path) {\n            Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_file() => {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    \"product manifest path must be a regular file\",\n                ));\n            }\n            Ok(_) if fs::read(&path)?.as_slice() == manifest.as_bytes() => return Ok(path),\n            Ok(_) => {}\n            Err(error) if error.kind() == io::ErrorKind::NotFound => {}\n            Err(error) => return Err(error),\n        }\n        self.ensure_layout()?;\n        create_private_dir(&self.root.join(\"distributions\"))?;\n        let parent = path.parent().expect(\"manifest path has a parent\");\n        create_private_dir(parent)?;\n        let temporary = path.with_extension(\"toml.tmp\");\n        if let Ok(metadata) = fs::symlink_metadata(&temporary) {\n            if metadata.file_type().is_symlink() || !metadata.is_file() {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidInput,","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/crates/unicity-aos-bootstrap/src/lib.rs#L179-L215","documentation":"ensure_unicity_ce_manifest materializes the bundled manifest and then checks the product manifest path with symlink_metadata. If the path is a symlink or not a regular file, it returns InvalidInput with \"product manifest path must be a regular file\". This prevents an attacker or misconfiguration from pointing the product manifest at a symlinked or special file outside the managed root.","triggerScenarios":"Calling ensure_unicity_ce_manifest (directly or via foreground_daemon_command / ensure_runtime_available) when the manifest path exists as a symlink, directory, device node, or other non-regular file.","commonSituations":"An admin replaced the manifest with a symlink to a shared config to 'centralize' it; a packaging script linked the file; leftover state from a previous layout migration; tampering or a broken install.","solutions":["Delete the symlink/special file at the manifest path so ensure_unicity_ce_manifest can materialize the bundled manifest itself.","Reinstall or restore the runtime bundle so the product path is a real regular file.","Verify with `ls -la` that the path is not a symlink (the 'l' in permissions) and is a regular file.","Remove any scripts or config management that symlink files inside the managed root."],"exampleFix":"// before (shell)\nln -sf /etc/shared/product.toml $ROOT/product.toml\n// after\nrm -f $ROOT/product.toml  # let ensure_unicity_ce_manifest materialize it\nrm -f $ROOT/product.toml && ensure_unicity_ce_manifest()?","handlingStrategy":"validation","validationCode":"fn manifest_path_is_clean(path: &Path) -> Result<(), String> {\n    match std::fs::symlink_metadata(path) {\n        Ok(m) if m.file_type().is_symlink() => Err(format!(\"{:?} is a symlink; remove it\", path)),\n        Ok(m) if !m.is_file() => Err(format!(\"{:?} is not a regular file; remove it\", path)),\n        _ => Ok(()),\n    }\n}","typeGuard":"fn is_clean_regular_file(path: &Path) -> bool {\n    std::fs::symlink_metadata(path)\n        .map(|m| m.is_file() && !m.file_type().is_symlink())\n        .unwrap_or(false)\n}","tryCatchPattern":"if let Err(e) = runtime.ensure_unicity_ce_manifest() {\n    if e.kind() == io::ErrorKind::InvalidInput && e.to_string().contains(\"product manifest\") {\n        eprintln!(\"manifest path is a symlink/special file; remove it and rerun\");\n    } else { return Err(e.into()); }\n}","preventionTips":["Never symlink files inside the managed runtime root, even for convenience.","After aborted installs or migrations, check for symlinks/special files with `find <root> -type l`.","Restrict write access to the runtime root with private directory permissions so it cannot be tampered with."],"tags":["symlink","security","filesystem","rust"],"backgroundTag":"symlink-detected","analyzedSha":"f6f22024fb1e8d122f28a1b4a9f75aee448ae839","analyzedAt":"2026-09-13T03:04:44.565Z","contentChangedAt":"2026-09-13T03:04:44.565Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}