{"record":{"id":"3285808d4b6de208","repo":"astrid-runtime/astrid","slug":"client-effective-token-validation-requires-the-ser","errorCode":null,"errorMessage":"client effective-token validation requires the server pipe end","messagePattern":"client effective-token validation requires the server pipe end","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/local_transport/windows.rs","lineNumber":484,"sourceCode":"}\n\nfn require_current_user_effective_client(stream: &LocalStream) -> io::Result<()> {\n    let client_sid = effective_client_user_sid(stream)?;\n    if client_sid.equals(&current_user_sid()?) {\n        Ok(())\n    } else {\n        Err(io::Error::new(\n            io::ErrorKind::PermissionDenied,\n            \"named-pipe client's effective token belongs to a different operating-system user\",\n        ))\n    }\n}\n\nfn effective_client_user_sid(stream: &LocalStream) -> io::Result<OwnedSid> {\n    let server = match &stream.inner {\n        StreamInner::Server(server) => server,\n        StreamInner::Client(_) => {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"client effective-token validation requires the server pipe end\",\n            ));\n        },\n    };\n    let impersonated = unsafe { ImpersonateNamedPipeClient(server.as_raw_handle().cast()) };\n    if impersonated == 0 {\n        return Err(last_error(\n            \"failed to impersonate the connected named-pipe client\",\n        ));\n    }\n    let guard = ImpersonationGuard { active: true };\n\n    let mut token = ptr::null_mut();\n    let opened = unsafe { OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, 1, &raw mut token) };\n    if opened == 0 || token.is_null() {\n        return Err(last_error(\n            \"failed to open impersonated named-pipe client token\",","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/local_transport/windows.rs#L466-L502","documentation":"effective_client_user_sid works by impersonating the client, which is only possible from the server end of a named pipe (it needs the server handle obtained from CreateNamedPipe). When handed a client-end LocalStream it returns InvalidInput, because effective-token validation is meaningless from the client side.","triggerScenarios":"Calling peer_is_current_user or require_current_user_effective_client on a LocalStream created by connect() (StreamInner::Client) rather than one obtained from accept() on a bound server (StreamInner::Server).","commonSituations":"Client code that reuses a shared validation helper on its own stream; copying server-side auth checks into client code; wrapping connect() and accept() results in the same type and losing track of which end you hold.","solutions":["Only call effective-token validation on streams returned by accept() on the server side.","On the client end, validate the server differently (e.g. process-owner checks) or skip effective-token checks entirely.","Track which end of the pipe you hold; if you need the check, forward the server stream handle to the code performing validation."],"exampleFix":"// before\nlet stream = LocalTransport::connect(&path)?;\nlet sid = peer_is_current_user(&stream)?; // InvalidInput\n// after\nlet stream = listener.accept()?; // server end only\nlet sid = peer_is_current_user(&stream)?;","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn is_server_end(stream: &LocalStream) -> bool {\n    matches!(stream.inner, StreamInner::Server(_))\n}","tryCatchPattern":"if !is_server_end(&stream) {\n    return Err(io::Error::new(\n        io::ErrorKind::InvalidInput,\n        \"effective-token check is only valid on the server pipe end\",\n    ));\n}","preventionTips":["Only perform peer/effective-token validation on streams from accept().","Wrap client and server streams in distinct newtypes so ends can't be confused.","Centralize auth checks behind a server-side API instead of free functions."],"tags":["windows","named-pipes","api-misuse","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}