{"record":{"id":"7152f206f3980652","repo":"Hmbown/CodeWhale","slug":"refusing-unauthenticated-app-server-bind-on-non-lo","errorCode":null,"errorMessage":"refusing unauthenticated app-server bind on non-loopback address","messagePattern":"refusing unauthenticated app-server bind on non-loopback address","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/app-server/src/lib.rs","lineNumber":694,"sourceCode":"        stdio_bridge: Arc::new(Mutex::new(None)),\n        stdio_thread_hints: Arc::new(Mutex::new(HashMap::new())),\n        pending_user_input: Arc::new(Mutex::new(std::collections::HashMap::new())),\n        in_flight_turns: Arc::new(Mutex::new(HashMap::new())),\n    })\n}\n\nfn resolve_auth_token(options: &AppServerOptions) -> Result<Option<String>> {\n    let configured = options.auth_token.as_ref().map(|token| token.trim());\n    if let Some(token) = configured\n        && token.is_empty()\n    {\n        bail!(\"app-server auth token cannot be empty\");\n    }\n    let has_explicit_token = configured.is_some();\n\n    if options.insecure_no_auth {\n        if !options.listen.ip().is_loopback() {\n            bail!(\"refusing unauthenticated app-server bind on non-loopback address\");\n        }\n        eprintln!(\"warning: app-server HTTP auth disabled by --insecure-no-auth\");\n        return Ok(None);\n    }\n\n    if !has_explicit_token && !options.listen.ip().is_loopback() {\n        bail!(\n            \"refusing non-loopback app-server bind without explicit auth token; pass --auth-token or set CODEWHALE_APP_SERVER_TOKEN\"\n        );\n    }\n\n    let token = configured\n        .map(str::to_string)\n        .unwrap_or_else(|| format!(\"cwapp_{}\", Uuid::new_v4().simple()));\n    for line in app_server_auth_status_lines(has_explicit_token) {\n        eprintln!(\"{line}\");\n    }\n    Ok(Some(token))","sourceCodeStart":676,"sourceCodeEnd":712,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/app-server/src/lib.rs#L676-L712","documentation":"During plugin review on Windows, open_bundle_file opens bundle files with FILE_FLAG_OPEN_REPARSE_POINT and a deny-write share mode, then requires a regular file, no FILE_ATTRIBUTE_REPARSE_POINT, and exactly one link (windows_file_identity). A symlinked leaf, a hard-linked file, or a non-file is rejected with InvalidData; the sharing mode also blocks replacement while the file is hashed.","triggerScenarios":"Reviewing or staging a plugin whose component file is a symlink, has a second hard link (identity.links != 1), or names a directory/device - typically a hand-installed, deduped, or tampered plugin bundle.","commonSituations":"Users symlink plugin files to share one copy between installations; migration tools hard-link bundle files; partially synced cloud folders leave reparse placeholders in the plugins tree.","solutions":["Reinstall the plugin through Codewhale so the bundle is staged as plain single-link files","Inspect the path: 'fsutil reparsepoint query <file>' and 'fsutil hardlink list <file>', then remove extra links or links entirely","Stop managing the plugins directory with symlink/hard-link dedupe tools"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x400;\n\nfn is_plain_single_link_file(path: &std::path::Path) -> bool {\n    match std::fs::symlink_metadata(path) {\n        Ok(md) => md.is_file() && md.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT == 0,\n        Err(_) => false,\n    }\n    // plus: hard-link count == 1 via GetFileInformationByHandle / fsutil hardlink list\n}","typeGuard":null,"tryCatchPattern":"match open_bundle_file(&path) {\n    Err(e) if e.to_string().contains(\"reparse point, hard link\") => {\n        // reinstall the plugin so the bundle is restaged as plain files\n    }\n    other => other?,\n}","preventionTips":["Install plugins only through Codewhale's own install path","Treat the plugins directory as app-managed; no symlink or hard-link dedupe","Exclude the plugins directory from cloud sync"],"tags":["plugins","windows","symlink","hard-link","reparse-point"],"backgroundTag":"symlink-security-check","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}