Sign In

@quantakrypto/qprobe

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@quantakrypto/qprobe - npm Package Compare versions

Comparing version
0.9.0
to
0.10.0
+1
-1
dist/args.d.ts

@@ -18,4 +18,4 @@ /**

export declare function parseArgs(argv: readonly string[]): CliArgs;
export declare const HELP = "qprobe \u2014 active post-quantum readiness probing of live TLS/SSH endpoints you OWN.\n\nUSAGE\n qprobe [options] <host[:port]> [host[:port] ...]\n\nAUTHORIZATION (required \u2014 one of)\n --i-own-this Attest you are authorized to probe the given endpoint(s).\n --owned-hosts <file> Ownership manifest (one host per line, # comments).\n\n Active probing of endpoints you do not own may be unlawful. qprobe refuses CIDR\n blocks, IP ranges, wildcards and target lists. See THREAT-MODEL.md.\n\nOPTIONS\n --tls | --ssh | --smtp | --imap | --pop3 | --postgres\n Force a probe mode (default: auto by well-known port \u2014\n SSH :22, SMTP :25/:587, IMAP :143, POP3 :110,\n PostgreSQL :5432, else direct TLS \u2014 which covers HTTPS\n :443, IMAPS :993, DoT :853).\n --servername <name> TLS SNI server name (default: the host).\n --timeout <ms> Per-connection timeout (default: 8000).\n --format <human|json|sarif|cbom>\n Output format (default: human). --json / --sarif / --cbom\n are aliases. SARIF 2.1.0 and CycloneDX 1.6 CBOM compose\n with qScan output (same formats).\n -h, --help Show this help.\n\nEXAMPLES\n qprobe --i-own-this example.com # TLS 443: hybrid KEX + cert posture\n qprobe --ssh --i-own-this git.example.com # SSH 22: KEXINIT algorithms\n qprobe --owned-hosts hosts.txt api.example.com:8443 --json\n\nqprobe reports the negotiated reality; it never modifies an endpoint (\"engine disposes\").";
export declare const HELP = "qprobe \u2014 active post-quantum readiness probing of live TLS/SSH endpoints you OWN.\n\nUSAGE\n qprobe [options] <host[:port]> [host[:port] ...]\n\nAUTHORIZATION (required \u2014 one of)\n --i-own-this Attest you are authorized to probe the given endpoint(s).\n --owned-hosts <file> Ownership manifest (one host per line, # comments).\n\n A target is one host or host:port, e.g. api.example.com \u2014 not a URL.\n Active probing of endpoints you do not own may be unlawful. qprobe refuses CIDR\n blocks, IP ranges, wildcards and target lists. See THREAT-MODEL.md.\n\nOPTIONS\n --tls | --ssh | --smtp | --imap | --pop3 | --postgres\n Force a probe mode (default: auto by well-known port \u2014\n SSH :22, SMTP :25/:587, IMAP :143, POP3 :110,\n PostgreSQL :5432, else direct TLS \u2014 which covers HTTPS\n :443, IMAPS :993, DoT :853).\n --servername <name> TLS SNI server name (default: the host).\n --timeout <ms> Per-connection timeout (default: 8000).\n --format <human|json|sarif|cbom>\n Output format (default: human). --json / --sarif / --cbom\n are aliases. SARIF 2.1.0 and CycloneDX 1.6 CBOM compose\n with qScan output (same formats).\n -h, --help Show this help.\n\nEXAMPLES\n qprobe --i-own-this example.com # TLS 443: hybrid KEX + cert posture\n qprobe --ssh --i-own-this git.example.com # SSH 22: KEXINIT algorithms\n qprobe --owned-hosts hosts.txt api.example.com:8443 --json\n\nqprobe reports the negotiated reality; it never modifies an endpoint (\"engine disposes\").";
export {};
//# sourceMappingURL=args.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,KAAK,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;AACnF,KAAK,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAErD,UAAU,OAAO;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAkE1D;AAED,eAAO,MAAM,IAAI,ioDA+ByE,CAAC"}
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,KAAK,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;AACnF,KAAK,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAErD,UAAU,OAAO;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAkE1D;AAED,eAAO,MAAM,IAAI,8sDAgCyE,CAAC"}

@@ -83,2 +83,3 @@ /**

A target is one host or host:port, e.g. api.example.com — not a URL.
Active probing of endpoints you do not own may be unlawful. qprobe refuses CIDR

@@ -85,0 +86,0 @@ blocks, IP ranges, wildcards and target lists. See THREAT-MODEL.md.

@@ -1,1 +0,1 @@

{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,MAAM,UAAU,SAAS,CAAC,IAAuB;IAC/C,MAAM,IAAI,GAAY;QACpB,OAAO,EAAE,EAAE;QACX,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,KAAK;QACf,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,OAAO;QACf,IAAI,EAAE,KAAK;KACZ,CAAC;IACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,QAAQ,CAAC,EAAE,CAAC;YACV,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACjB,MAAM;YACR,KAAK,cAAc;gBACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;gBACrB,MAAM;YACR,KAAK,OAAO;gBACV,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;gBAClB,MAAM;YACR,KAAK,OAAO;gBACV,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;gBAClB,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;gBACnB,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;gBACnB,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;gBACnB,MAAM;YACR,KAAK,YAAY;gBACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;gBACvB,MAAM;YACR,KAAK,eAAe;gBAClB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAChC,MAAM;YACR,KAAK,cAAc;gBACjB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC5B,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC;gBACrD,MAAM;YACR,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACpB,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,OAAO;oBAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;gBACpF,MAAM;YACR,CAAC;YACD,KAAK,QAAQ;gBACX,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;gBACrB,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;gBACtB,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;gBACrB,MAAM;YACR;gBACE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClD,MAAM;QACV,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0FA+BsE,CAAC","sourcesContent":["/**\n * Hand-rolled CLI argument parser (zero dependencies), mirroring qScan's approach:\n * the parser is pure and total — it never throws and never does I/O.\n */\n\ntype ProbeModeArg = \"tls\" | \"ssh\" | \"smtp\" | \"imap\" | \"pop3\" | \"postgres\" | \"auto\";\ntype FormatArg = \"human\" | \"json\" | \"sarif\" | \"cbom\";\n\ninterface CliArgs {\n targets: string[];\n mode: ProbeModeArg;\n iOwnThis: boolean;\n ownedHostsFile?: string;\n servername?: string;\n timeoutMs: number;\n format: FormatArg;\n help: boolean;\n}\n\nexport function parseArgs(argv: readonly string[]): CliArgs {\n const args: CliArgs = {\n targets: [],\n mode: \"auto\",\n iOwnThis: false,\n timeoutMs: 8000,\n format: \"human\",\n help: false,\n };\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n switch (a) {\n case \"-h\":\n case \"--help\":\n args.help = true;\n break;\n case \"--i-own-this\":\n args.iOwnThis = true;\n break;\n case \"--tls\":\n args.mode = \"tls\";\n break;\n case \"--ssh\":\n args.mode = \"ssh\";\n break;\n case \"--smtp\":\n args.mode = \"smtp\";\n break;\n case \"--imap\":\n args.mode = \"imap\";\n break;\n case \"--pop3\":\n args.mode = \"pop3\";\n break;\n case \"--postgres\":\n args.mode = \"postgres\";\n break;\n case \"--owned-hosts\":\n args.ownedHostsFile = argv[++i];\n break;\n case \"--servername\":\n args.servername = argv[++i];\n break;\n case \"--timeout\":\n args.timeoutMs = Number(argv[++i]) || args.timeoutMs;\n break;\n case \"--format\": {\n const v = argv[++i];\n if (v === \"json\" || v === \"sarif\" || v === \"cbom\" || v === \"human\") args.format = v;\n break;\n }\n case \"--json\":\n args.format = \"json\";\n break;\n case \"--sarif\":\n args.format = \"sarif\";\n break;\n case \"--cbom\":\n args.format = \"cbom\";\n break;\n default:\n if (a && !a.startsWith(\"-\")) args.targets.push(a);\n break;\n }\n }\n return args;\n}\n\nexport const HELP = `qprobe — active post-quantum readiness probing of live TLS/SSH endpoints you OWN.\n\nUSAGE\n qprobe [options] <host[:port]> [host[:port] ...]\n\nAUTHORIZATION (required — one of)\n --i-own-this Attest you are authorized to probe the given endpoint(s).\n --owned-hosts <file> Ownership manifest (one host per line, # comments).\n\n Active probing of endpoints you do not own may be unlawful. qprobe refuses CIDR\n blocks, IP ranges, wildcards and target lists. See THREAT-MODEL.md.\n\nOPTIONS\n --tls | --ssh | --smtp | --imap | --pop3 | --postgres\n Force a probe mode (default: auto by well-known port —\n SSH :22, SMTP :25/:587, IMAP :143, POP3 :110,\n PostgreSQL :5432, else direct TLS — which covers HTTPS\n :443, IMAPS :993, DoT :853).\n --servername <name> TLS SNI server name (default: the host).\n --timeout <ms> Per-connection timeout (default: 8000).\n --format <human|json|sarif|cbom>\n Output format (default: human). --json / --sarif / --cbom\n are aliases. SARIF 2.1.0 and CycloneDX 1.6 CBOM compose\n with qScan output (same formats).\n -h, --help Show this help.\n\nEXAMPLES\n qprobe --i-own-this example.com # TLS 443: hybrid KEX + cert posture\n qprobe --ssh --i-own-this git.example.com # SSH 22: KEXINIT algorithms\n qprobe --owned-hosts hosts.txt api.example.com:8443 --json\n\nqprobe reports the negotiated reality; it never modifies an endpoint (\"engine disposes\").`;\n"]}
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,MAAM,UAAU,SAAS,CAAC,IAAuB;IAC/C,MAAM,IAAI,GAAY;QACpB,OAAO,EAAE,EAAE;QACX,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,KAAK;QACf,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,OAAO;QACf,IAAI,EAAE,KAAK;KACZ,CAAC;IACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,QAAQ,CAAC,EAAE,CAAC;YACV,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACjB,MAAM;YACR,KAAK,cAAc;gBACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;gBACrB,MAAM;YACR,KAAK,OAAO;gBACV,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;gBAClB,MAAM;YACR,KAAK,OAAO;gBACV,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;gBAClB,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;gBACnB,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;gBACnB,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;gBACnB,MAAM;YACR,KAAK,YAAY;gBACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;gBACvB,MAAM;YACR,KAAK,eAAe;gBAClB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAChC,MAAM;YACR,KAAK,cAAc;gBACjB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC5B,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC;gBACrD,MAAM;YACR,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACpB,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,OAAO;oBAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;gBACpF,MAAM;YACR,CAAC;YACD,KAAK,QAAQ;gBACX,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;gBACrB,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;gBACtB,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;gBACrB,MAAM;YACR;gBACE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClD,MAAM;QACV,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0FAgCsE,CAAC","sourcesContent":["/**\n * Hand-rolled CLI argument parser (zero dependencies), mirroring qScan's approach:\n * the parser is pure and total — it never throws and never does I/O.\n */\n\ntype ProbeModeArg = \"tls\" | \"ssh\" | \"smtp\" | \"imap\" | \"pop3\" | \"postgres\" | \"auto\";\ntype FormatArg = \"human\" | \"json\" | \"sarif\" | \"cbom\";\n\ninterface CliArgs {\n targets: string[];\n mode: ProbeModeArg;\n iOwnThis: boolean;\n ownedHostsFile?: string;\n servername?: string;\n timeoutMs: number;\n format: FormatArg;\n help: boolean;\n}\n\nexport function parseArgs(argv: readonly string[]): CliArgs {\n const args: CliArgs = {\n targets: [],\n mode: \"auto\",\n iOwnThis: false,\n timeoutMs: 8000,\n format: \"human\",\n help: false,\n };\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n switch (a) {\n case \"-h\":\n case \"--help\":\n args.help = true;\n break;\n case \"--i-own-this\":\n args.iOwnThis = true;\n break;\n case \"--tls\":\n args.mode = \"tls\";\n break;\n case \"--ssh\":\n args.mode = \"ssh\";\n break;\n case \"--smtp\":\n args.mode = \"smtp\";\n break;\n case \"--imap\":\n args.mode = \"imap\";\n break;\n case \"--pop3\":\n args.mode = \"pop3\";\n break;\n case \"--postgres\":\n args.mode = \"postgres\";\n break;\n case \"--owned-hosts\":\n args.ownedHostsFile = argv[++i];\n break;\n case \"--servername\":\n args.servername = argv[++i];\n break;\n case \"--timeout\":\n args.timeoutMs = Number(argv[++i]) || args.timeoutMs;\n break;\n case \"--format\": {\n const v = argv[++i];\n if (v === \"json\" || v === \"sarif\" || v === \"cbom\" || v === \"human\") args.format = v;\n break;\n }\n case \"--json\":\n args.format = \"json\";\n break;\n case \"--sarif\":\n args.format = \"sarif\";\n break;\n case \"--cbom\":\n args.format = \"cbom\";\n break;\n default:\n if (a && !a.startsWith(\"-\")) args.targets.push(a);\n break;\n }\n }\n return args;\n}\n\nexport const HELP = `qprobe — active post-quantum readiness probing of live TLS/SSH endpoints you OWN.\n\nUSAGE\n qprobe [options] <host[:port]> [host[:port] ...]\n\nAUTHORIZATION (required — one of)\n --i-own-this Attest you are authorized to probe the given endpoint(s).\n --owned-hosts <file> Ownership manifest (one host per line, # comments).\n\n A target is one host or host:port, e.g. api.example.com — not a URL.\n Active probing of endpoints you do not own may be unlawful. qprobe refuses CIDR\n blocks, IP ranges, wildcards and target lists. See THREAT-MODEL.md.\n\nOPTIONS\n --tls | --ssh | --smtp | --imap | --pop3 | --postgres\n Force a probe mode (default: auto by well-known port —\n SSH :22, SMTP :25/:587, IMAP :143, POP3 :110,\n PostgreSQL :5432, else direct TLS — which covers HTTPS\n :443, IMAPS :993, DoT :853).\n --servername <name> TLS SNI server name (default: the host).\n --timeout <ms> Per-connection timeout (default: 8000).\n --format <human|json|sarif|cbom>\n Output format (default: human). --json / --sarif / --cbom\n are aliases. SARIF 2.1.0 and CycloneDX 1.6 CBOM compose\n with qScan output (same formats).\n -h, --help Show this help.\n\nEXAMPLES\n qprobe --i-own-this example.com # TLS 443: hybrid KEX + cert posture\n qprobe --ssh --i-own-this git.example.com # SSH 22: KEXINIT algorithms\n qprobe --owned-hosts hosts.txt api.example.com:8443 --json\n\nqprobe reports the negotiated reality; it never modifies an endpoint (\"engine disposes\").`;\n"]}

@@ -1,1 +0,1 @@

{"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,mCAAmC;AACnC,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAKD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAwDtE"}
{"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,mCAAmC;AACnC,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AA6BD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAqFtE"}

@@ -16,3 +16,25 @@ /**

const IP_RANGE = /^\d{1,3}(?:\.\d{1,3}){3}\s*-\s*\d{1,3}/;
/** A URI scheme prefix: `https://`, `ssh://`, `ldaps://`. */
const SCHEME = /^[a-z][a-z0-9+.-]*:\/\//i;
/** A CIDR suffix proper: a slash followed by a prefix length and nothing else. */
const CIDR_SUFFIX = /\/\d{1,3}$/;
/**
* The host someone probably meant, for the error message only.
*
* Never used to accept the input: a target has to be named as one host, because
* `--i-own-this` is an ownership attestation and `https://mine.com@theirs.com/`
* resolves to `theirs.com`. Suggesting the answer while still refusing keeps the
* attestation explicit and the fix one copy-paste away.
*/
function suggestHost(raw) {
try {
const url = new URL(SCHEME.test(raw) ? raw : `https://${raw}`);
const host = url.hostname.replace(/^\[|\]$/g, "");
return host || null;
}
catch {
return null;
}
}
/**
* Parse and validate a single target. Throws {@link TargetError} for anything

@@ -29,5 +51,26 @@ * that is not a single host / host:port — the refusal is a security control, not

throw new TargetError("empty target");
// A URL is still refused — a target is one named host, not something with a
// scheme, credentials and a path — but it must be refused for the right reason.
// This check has to come before the slash check below: `https://example.com`
// contains slashes, so it used to be reported as a CIDR block, which described
// a mistake the operator had not made.
if (SCHEME.test(raw)) {
const host = suggestHost(raw);
throw new TargetError(`refusing URL "${raw}" — qProbe takes a host, not a URL${host ? `. Try: ${host}` : "."}`);
}
// Credentials name one host to a reader and connect to another
// (`https://mine.com@theirs.com`), which an ownership attestation cannot allow.
if (raw.includes("@")) {
const host = suggestHost(raw);
throw new TargetError(`refusing target with credentials "${raw}" — name the host directly${host ? `. Try: ${host}` : "."}`);
}
// Range / sweep / list markers are refused BEFORE any parsing.
if (raw.includes("/")) {
throw new TargetError(`refusing CIDR block "${raw}" — qProbe probes one host at a time, not ranges.`);
// Distinguish an actual CIDR block from a path someone pasted: only a
// trailing prefix length is a range. Both are refused; only one is a sweep.
if (CIDR_SUFFIX.test(raw)) {
throw new TargetError(`refusing CIDR block "${raw}" — qProbe probes one host at a time, not ranges.`);
}
const host = suggestHost(raw);
throw new TargetError(`refusing target with a path "${raw}" — qProbe probes a host, not a URL path${host ? `. Try: ${host}` : "."}`);
}

@@ -34,0 +77,0 @@ if (raw.includes("*")) {

@@ -1,1 +0,1 @@

{"version":3,"file":"target.js","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Target parsing + validation. qProbe opens real network connections, so the\n * FIRST job is to refuse anything that looks like a scan of infrastructure the\n * operator has not named one host at a time: CIDR blocks, IP ranges, wildcards,\n * and comma-separated lists are rejected outright (see THREAT-MODEL.md). A target\n * is exactly one `host` or `host:port`.\n */\n\n/** A single validated endpoint. */\nexport interface Target {\n host: string;\n port: number;\n}\n\nexport class TargetError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TargetError\";\n }\n}\n\n/** IPv4 range in the last octet, e.g. `10.0.0.1-50`, or a full-range dash form. */\nconst IP_RANGE = /^\\d{1,3}(?:\\.\\d{1,3}){3}\\s*-\\s*\\d{1,3}/;\n\n/**\n * Parse and validate a single target. Throws {@link TargetError} for anything\n * that is not a single host / host:port — the refusal is a security control, not\n * a convenience check.\n *\n * @param input e.g. \"example.com\", \"example.com:8443\", \"10.0.0.5:22\"\n * @param defaultPort port to use when the input carries none\n */\nexport function parseTarget(input: string, defaultPort: number): Target {\n const raw = input.trim();\n if (raw === \"\") throw new TargetError(\"empty target\");\n\n // Range / sweep / list markers are refused BEFORE any parsing.\n if (raw.includes(\"/\")) {\n throw new TargetError(\n `refusing CIDR block \"${raw}\" — qProbe probes one host at a time, not ranges.`,\n );\n }\n if (raw.includes(\"*\")) {\n throw new TargetError(`refusing wildcard target \"${raw}\".`);\n }\n if (raw.includes(\",\")) {\n throw new TargetError(\n `refusing target list \"${raw}\" — pass one target per invocation / manifest line.`,\n );\n }\n if (/\\s/.test(raw)) {\n throw new TargetError(`invalid target \"${raw}\" (whitespace).`);\n }\n if (IP_RANGE.test(raw)) {\n throw new TargetError(`refusing IP range \"${raw}\" — qProbe probes one host at a time.`);\n }\n\n // Split host:port (IPv6 in brackets is supported: [::1]:443).\n let host: string;\n let portStr: string | undefined;\n if (raw.startsWith(\"[\")) {\n const end = raw.indexOf(\"]\");\n if (end < 0) throw new TargetError(`invalid IPv6 target \"${raw}\" (missing ]).`);\n host = raw.slice(1, end);\n const rest = raw.slice(end + 1);\n if (rest.startsWith(\":\")) portStr = rest.slice(1);\n else if (rest !== \"\") throw new TargetError(`invalid target \"${raw}\".`);\n } else {\n const idx = raw.lastIndexOf(\":\");\n // A single ':' → host:port. Multiple ':' with no brackets → bare IPv6 (no port).\n if (idx >= 0 && raw.indexOf(\":\") === idx) {\n host = raw.slice(0, idx);\n portStr = raw.slice(idx + 1);\n } else {\n host = raw;\n }\n }\n\n if (host === \"\") throw new TargetError(`invalid target \"${raw}\" (empty host).`);\n\n let port = defaultPort;\n if (portStr !== undefined) {\n if (!/^\\d+$/.test(portStr)) throw new TargetError(`invalid port \"${portStr}\" in \"${raw}\".`);\n port = Number(portStr);\n if (port < 1 || port > 65535) throw new TargetError(`port out of range in \"${raw}\".`);\n }\n\n return { host, port };\n}\n"]}
{"version":3,"file":"target.js","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Target parsing + validation. qProbe opens real network connections, so the\n * FIRST job is to refuse anything that looks like a scan of infrastructure the\n * operator has not named one host at a time: CIDR blocks, IP ranges, wildcards,\n * and comma-separated lists are rejected outright (see THREAT-MODEL.md). A target\n * is exactly one `host` or `host:port`.\n */\n\n/** A single validated endpoint. */\nexport interface Target {\n host: string;\n port: number;\n}\n\nexport class TargetError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TargetError\";\n }\n}\n\n/** IPv4 range in the last octet, e.g. `10.0.0.1-50`, or a full-range dash form. */\nconst IP_RANGE = /^\\d{1,3}(?:\\.\\d{1,3}){3}\\s*-\\s*\\d{1,3}/;\n\n/** A URI scheme prefix: `https://`, `ssh://`, `ldaps://`. */\nconst SCHEME = /^[a-z][a-z0-9+.-]*:\\/\\//i;\n\n/** A CIDR suffix proper: a slash followed by a prefix length and nothing else. */\nconst CIDR_SUFFIX = /\\/\\d{1,3}$/;\n\n/**\n * The host someone probably meant, for the error message only.\n *\n * Never used to accept the input: a target has to be named as one host, because\n * `--i-own-this` is an ownership attestation and `https://mine.com@theirs.com/`\n * resolves to `theirs.com`. Suggesting the answer while still refusing keeps the\n * attestation explicit and the fix one copy-paste away.\n */\nfunction suggestHost(raw: string): string | null {\n try {\n const url = new URL(SCHEME.test(raw) ? raw : `https://${raw}`);\n const host = url.hostname.replace(/^\\[|\\]$/g, \"\");\n return host || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse and validate a single target. Throws {@link TargetError} for anything\n * that is not a single host / host:port — the refusal is a security control, not\n * a convenience check.\n *\n * @param input e.g. \"example.com\", \"example.com:8443\", \"10.0.0.5:22\"\n * @param defaultPort port to use when the input carries none\n */\nexport function parseTarget(input: string, defaultPort: number): Target {\n const raw = input.trim();\n if (raw === \"\") throw new TargetError(\"empty target\");\n\n // A URL is still refused — a target is one named host, not something with a\n // scheme, credentials and a path — but it must be refused for the right reason.\n // This check has to come before the slash check below: `https://example.com`\n // contains slashes, so it used to be reported as a CIDR block, which described\n // a mistake the operator had not made.\n if (SCHEME.test(raw)) {\n const host = suggestHost(raw);\n throw new TargetError(\n `refusing URL \"${raw}\" — qProbe takes a host, not a URL${host ? `. Try: ${host}` : \".\"}`,\n );\n }\n\n // Credentials name one host to a reader and connect to another\n // (`https://mine.com@theirs.com`), which an ownership attestation cannot allow.\n if (raw.includes(\"@\")) {\n const host = suggestHost(raw);\n throw new TargetError(\n `refusing target with credentials \"${raw}\" — name the host directly${host ? `. Try: ${host}` : \".\"}`,\n );\n }\n\n // Range / sweep / list markers are refused BEFORE any parsing.\n if (raw.includes(\"/\")) {\n // Distinguish an actual CIDR block from a path someone pasted: only a\n // trailing prefix length is a range. Both are refused; only one is a sweep.\n if (CIDR_SUFFIX.test(raw)) {\n throw new TargetError(\n `refusing CIDR block \"${raw}\" — qProbe probes one host at a time, not ranges.`,\n );\n }\n const host = suggestHost(raw);\n throw new TargetError(\n `refusing target with a path \"${raw}\" — qProbe probes a host, not a URL path${host ? `. Try: ${host}` : \".\"}`,\n );\n }\n if (raw.includes(\"*\")) {\n throw new TargetError(`refusing wildcard target \"${raw}\".`);\n }\n if (raw.includes(\",\")) {\n throw new TargetError(\n `refusing target list \"${raw}\" — pass one target per invocation / manifest line.`,\n );\n }\n if (/\\s/.test(raw)) {\n throw new TargetError(`invalid target \"${raw}\" (whitespace).`);\n }\n if (IP_RANGE.test(raw)) {\n throw new TargetError(`refusing IP range \"${raw}\" — qProbe probes one host at a time.`);\n }\n\n // Split host:port (IPv6 in brackets is supported: [::1]:443).\n let host: string;\n let portStr: string | undefined;\n if (raw.startsWith(\"[\")) {\n const end = raw.indexOf(\"]\");\n if (end < 0) throw new TargetError(`invalid IPv6 target \"${raw}\" (missing ]).`);\n host = raw.slice(1, end);\n const rest = raw.slice(end + 1);\n if (rest.startsWith(\":\")) portStr = rest.slice(1);\n else if (rest !== \"\") throw new TargetError(`invalid target \"${raw}\".`);\n } else {\n const idx = raw.lastIndexOf(\":\");\n // A single ':' → host:port. Multiple ':' with no brackets → bare IPv6 (no port).\n if (idx >= 0 && raw.indexOf(\":\") === idx) {\n host = raw.slice(0, idx);\n portStr = raw.slice(idx + 1);\n } else {\n host = raw;\n }\n }\n\n if (host === \"\") throw new TargetError(`invalid target \"${raw}\" (empty host).`);\n\n let port = defaultPort;\n if (portStr !== undefined) {\n if (!/^\\d+$/.test(portStr)) throw new TargetError(`invalid port \"${portStr}\" in \"${raw}\".`);\n port = Number(portStr);\n if (port < 1 || port > 65535) throw new TargetError(`port out of range in \"${raw}\".`);\n }\n\n return { host, port };\n}\n"]}
{
"name": "@quantakrypto/qprobe",
"version": "0.9.0",
"version": "0.10.0",
"description": "qProbe — actively inspect live TLS/SSH endpoints you OWN for post-quantum readiness (hybrid KEX, classical certs). Gated behind an ownership attestation. Zero runtime dependencies.",

@@ -40,3 +40,3 @@ "license": "Apache-2.0",

"dependencies": {
"@quantakrypto/core": "0.9.0"
"@quantakrypto/core": "0.10.0"
},

@@ -43,0 +43,0 @@ "scripts": {

@@ -15,3 +15,4 @@ # @quantakrypto/qprobe

> authorized to test the target. Active probing of endpoints you do not own may be
> unlawful. qProbe refuses CIDR blocks, IP ranges, wildcards, and target lists — it
> unlawful. A target is one host or `host:port` (`api.example.com`), never a URL.
> qProbe refuses CIDR blocks, IP ranges, wildcards, and target lists — it
> probes **one host you operate** at a time, and it only ever performs a benign,

@@ -93,3 +94,3 @@ > unauthenticated handshake. It reports the negotiated reality; it never modifies an

qscan . --cbom -o code-infra.cbom.json # code + infra crypto assets
qprobe --owned-hosts hosts.txt api.example.com --cbom -o endpoints.cbom.json
qprobe --owned-hosts hosts.txt api.example.com --cbom > endpoints.cbom.json
```

@@ -96,0 +97,0 @@