{"record":{"id":"0c9df1ab980ae510","repo":"tinyhumansai/openhuman","slug":"invalid-paramname-string-value-type-must","errorCode":null,"errorMessage":"Invalid ${paramName}: ${String(value)}. Type must be an integer or a string.","messagePattern":"Invalid (.+?): (.+?)\\. Type must be an integer or a string\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"critical","filePath":"app/src/lib/mcp/validation.ts","lineNumber":46,"sourceCode":"    if (!Number.isNaN(intValue) && Number.isFinite(intValue)) {\n      if (intValue < -(2 ** 63) || intValue > 2 ** 63 - 1) {\n        throw new ValidationError(\n          `Invalid ${paramName}: ${value}. ID is out of the valid integer range.`\n        );\n      }\n      return intValue;\n    }\n\n    if (/^@?[a-zA-Z0-9_]{5,}$/.test(value)) {\n      return value.startsWith('@') ? value : `@${value}`;\n    }\n\n    throw new ValidationError(\n      `Invalid ${paramName}: '${value}'. Must be a valid integer ID or a username string.`\n    );\n  }\n\n  throw new ValidationError(\n    `Invalid ${paramName}: ${String(value)}. Type must be an integer or a string.`\n  );\n}\n\n/**\n * Validate list of IDs\n */\nexport function validateIdList(value: unknown, paramName: string): Array<number | string> {\n  if (!Array.isArray(value)) {\n    throw new ValidationError(`Invalid ${paramName}: must be an array of IDs.`);\n  }\n\n  return value.map((item: unknown, index: number) => {\n    try {\n      return validateId(item, `${paramName}[${index}]`);\n    } catch (error) {\n      if (error instanceof ValidationError) {\n        throw error;","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/lib/mcp/validation.ts#L28-L64","documentation":"Fail-closed security guard in the RPC HTTP bind path (src/core/runtime/builder.rs:692). Binding to a non-loopback host requires an operator-supplied token via the OPENHUMAN_CORE_TOKEN env var (core::auth::CORE_TOKEN_ENV_VAR). The auto-generated {workspace}/core.token file only authenticates local clients — remote clients cannot read a workspace file — so a public bind relying on it would be effectively unauthenticated. The guard prints a [SECURITY] banner to stderr and aborts startup. An in-memory bearer via run_server_embedded_with_ready(rpc_token: Some(_)) also satisfies it.","triggerScenarios":"Config or env sets the RPC host to 0.0.0.0 / a LAN IP / a public DNS name (docker -p, LAN device access such as the iOS client, cloud hosting) while OPENHUMAN_CORE_TOKEN is unset; copying a server config to a machine where the env var is not exported; port-forwarding setups that expose the loopback listener.","commonSituations":"Docker/cloud deployments; LAN connectivity testing for mobile clients; a colleague's working config replicated without the accompanying env var; misreading core.token as sufficient for remote auth.","solutions":["Set OPENHUMAN_CORE_TOKEN to a strong secret on the server (`export OPENHUMAN_CORE_TOKEN=$(openssl rand -hex 32)`) and distribute it to clients via a secret manager","Or bind loopback (127.0.0.1) and expose remotely through an authenticated tunnel/proxy instead","For embedded cores, hand the bearer in-memory: run_server_embedded_with_ready(rpc_token: Some(...)) instead of relying on the file token","Never treat {workspace}/core.token as valid for non-loopback binds — that is exactly what this guard enforces"],"exampleFix":"# before (aborts: [SECURITY] Refusing to bind on 0.0.0.0 ...)\nOPENHUMAN_CORE_HOST=0.0.0.0 ./target/debug/openhuman-core serve\n\n# after\nexport OPENHUMAN_CORE_TOKEN=\"$(openssl rand -hex 32)\"\nOPENHUMAN_CORE_HOST=0.0.0.0 ./target/debug/openhuman-core serve","handlingStrategy":"validation","validationCode":"# bash: pre-flight the token whenever the bind host is non-loopback\nis_loopback() { case \"$1\" in 127.0.0.1|localhost|::1|\\[::1\\]) return 0;; *) return 1;; esac; }\nRPC_HOST=\"${OPENHUMAN_CORE_HOST:-127.0.0.1}\"\nif ! is_loopback \"$RPC_HOST\" && [ -z \"${OPENHUMAN_CORE_TOKEN:-}\" ]; then\n  echo \"refusing to start: non-loopback bind ($RPC_HOST) without OPENHUMAN_CORE_TOKEN\" >&2; exit 78\nfi\nexec ./target/debug/openhuman-core serve","typeGuard":"is_loopback() { case \"$1\" in 127.0.0.1|localhost|::1|\\[::1\\]) return 0;; *) return 1;; esac; }","tryCatchPattern":"# in the service supervisor / wrapper\nif ! out=$(./openhuman-core serve 2>&1); then\n  case \"$out\" in *\"Refusing to bind on\"*)\n    echo \"generate a token: export OPENHUMAN_CORE_TOKEN=\\\"$(openssl rand -hex 32)\\\" (and give it to clients), or bind 127.0.0.1\" >&2\n    exit 78 ;;\n  esac\n  printf '%s\\n' \"$out\" >&2; exit 1\nfi","preventionTips":["Never rely on {workspace}/core.token for non-loopback binds — remote clients cannot read it","Provision OPENHUMAN_CORE_TOKEN via the deployment's secret mechanism (systemd EnvironmentFile, docker secrets, vault) alongside the host config","Add a pre-start healthcheck asserting: non-loopback host implies token present","For embedded cores prefer the in-memory bearer (rpc_token: Some(..)) over env vars"],"tags":["security","network-binding","configuration","rpc","authentication","deployment"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}