{"record":{"id":"a6aa9d80a572ef2f","repo":"projectdiscovery/nuclei","slug":"grpc-target-host-cannot-be-empty","errorCode":null,"errorMessage":"grpc target host cannot be empty","messagePattern":"grpc target host cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/js/libs/grpc/invoke.go","lineNumber":48,"sourceCode":"\tinsecureSkipVerify bool\n\tserverName         string\n\tmaxRecvMsgSize     int\n}\n\n// dialTarget builds a *grpc.ClientConn whose every connection is routed through\n// nuclei's network policy. The host is validated up front and the actual dial\n// is delegated to the execution's fastdialer via a custom context dialer, so\n// IP/host denylists and RestrictLocalNetworkAccess are always enforced. The\n// passthrough scheme guarantees the target is handed verbatim to our dialer\n// (instead of gRPC's built in DNS resolver), keeping resolution and policy\n// enforcement inside fastdialer.\nfunc dialTarget(ctx context.Context, executionID, target string, cfg connConfig) (*grpc.ClientConn, error) {\n\thost, _, err := net.SplitHostPort(target)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid grpc target %q (expected host:port): %w\", target, err)\n\t}\n\tif host == \"\" {\n\t\treturn nil, fmt.Errorf(\"grpc target host cannot be empty\")\n\t}\n\tif executionID == \"\" {\n\t\treturn nil, fmt.Errorf(\"grpc: refusing to dial without executionId\")\n\t}\n\tif !protocolstate.IsHostAllowed(executionID, host) {\n\t\treturn nil, protocolstate.ErrHostDenied.Msgf(host)\n\t}\n\tdialers := protocolstate.GetDialersWithId(executionID)\n\tif dialers == nil || dialers.Fastdialer == nil {\n\t\treturn nil, fmt.Errorf(\"grpc: dialers not initialized for executionId %q\", executionID)\n\t}\n\n\tcontextDialer := func(dialCtx context.Context, addr string) (net.Conn, error) {\n\t\treturn dialers.Fastdialer.Dial(dialCtx, \"tcp\", addr)\n\t}\n\n\tvar creds credentials.TransportCredentials\n\tif cfg.plaintext {","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/projectdiscovery/nuclei/blob/265b3a3dec374741614e342f813c10f8b38d2bb7/pkg/js/libs/grpc/invoke.go#L30-L66","documentation":"net.SplitHostPort succeeded but the host portion is empty: the target starts with ':' (e.g. ':443' or ':50051'). There is no hostname to enforce the network policy (denylists, RestrictLocalNetworkAccess) against, so dialTarget refuses to continue.","triggerScenarios":"new grpc.Client(':443'); building the target by concatenation when the host variable is empty: `${host}:443` with host=''; whitespace-only host; ports-only config strings.","commonSituations":"Template extractors producing an empty host (match group missing); environment config with the host key unset but the port set; YAML anchors defaulting host to ''.","solutions":["Supply a concrete host/IP: new grpc.Client('grpc.acme.com:443')","Guard template flow: only construct the Client when the extracted host is non-empty","Validate host and port separately in config before joining them"],"exampleFix":"// before\nconst client = new grpc.Client(`:${port}`);\n\n// after: skip when the extractor found no host\nif (!host) { return; }\nconst client = new grpc.Client(`${host}:${port}`);","handlingStrategy":"validation","validationCode":"const hostOf = (t) => /^\\[.*\\]/.test(t) ? t.slice(1, t.indexOf(']')) : String(t || '').split(':')[0];\nif (!hostOf(target)) {\n  // extracted host empty: skip or fail the input, do not construct the client\n}","typeGuard":"const hasGrpcHost = (t) => !!t && !t.trim().startsWith(':') && /^[[\\w.-]/.test(t.trim());","tryCatchPattern":"try { const c = new grpc.Client(target); }\ncatch (e) { if (/host cannot be empty/.test(e.message || '')) { /* fix the empty host variable */ } }","preventionTips":["Validate host and port as separate non-empty values in config before joining","Skip the gRPC probe when the extractor produced no host","Beware YAML defaults that leave host as an empty string"],"tags":["grpc","javascript","validation"],"backgroundTag":null,"analyzedSha":"265b3a3dec374741614e342f813c10f8b38d2bb7","analyzedAt":"2026-08-15T20:05:51.855Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}