postquant 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +109 -0
- package/dist/commands/scan.d.ts +3 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +88 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/output/json.d.ts +3 -0
- package/dist/output/json.d.ts.map +1 -0
- package/dist/output/json.js +28 -0
- package/dist/output/json.js.map +1 -0
- package/dist/output/terminal.d.ts +3 -0
- package/dist/output/terminal.d.ts.map +1 -0
- package/dist/output/terminal.js +103 -0
- package/dist/output/terminal.js.map +1 -0
- package/dist/scanner/classifier.d.ts +3 -0
- package/dist/scanner/classifier.d.ts.map +1 -0
- package/dist/scanner/classifier.js +283 -0
- package/dist/scanner/classifier.js.map +1 -0
- package/dist/scanner/grader.d.ts +4 -0
- package/dist/scanner/grader.d.ts.map +1 -0
- package/dist/scanner/grader.js +49 -0
- package/dist/scanner/grader.js.map +1 -0
- package/dist/scanner/tls.d.ts +3 -0
- package/dist/scanner/tls.d.ts.map +1 -0
- package/dist/scanner/tls.js +122 -0
- package/dist/scanner/tls.js.map +1 -0
- package/dist/types/index.d.ts +72 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +52 -6
- package/index.js +0 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PostQuant Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# PostQuant
|
|
2
|
+
|
|
3
|
+
**Find quantum-vulnerable cryptography in your TLS endpoints.**
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.npmjs.com/package/postquant)
|
|
7
|
+
|
|
8
|
+
PostQuant scans TLS connections and reports which algorithms are vulnerable to quantum attacks. It grades endpoints A+ through F and tells you what to migrate to.
|
|
9
|
+
|
|
10
|
+
> **Early development.** The TLS scanner works. Code scanning and CBOM generation are planned.
|
|
11
|
+
|
|
12
|
+
## Why
|
|
13
|
+
|
|
14
|
+
NIST will **deprecate** RSA, ECC, and other quantum-vulnerable algorithms by **2030** and **disallow** them by **2035**. Adversaries are already harvesting encrypted traffic to decrypt later with quantum computers.
|
|
15
|
+
|
|
16
|
+
PostQuant shows you what's exposed.
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx postquant scan example.com
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Output:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Overall Grade: C
|
|
28
|
+
|
|
29
|
+
Certificate
|
|
30
|
+
Algorithm: ECDSA P-256 🔴 Quantum Vulnerable
|
|
31
|
+
|
|
32
|
+
Connection
|
|
33
|
+
Protocol: TLS 1.3 🟢 Current
|
|
34
|
+
Key Exchange: X25519 🔴 Quantum Vulnerable
|
|
35
|
+
Cipher: AES-256-GCM 🟢 Quantum Safe
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Most sites today score C or D. That's expected — almost nobody has deployed post-quantum cryptography yet.
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Scan a single host
|
|
44
|
+
postquant scan example.com
|
|
45
|
+
|
|
46
|
+
# Scan with explicit port
|
|
47
|
+
postquant scan example.com:8443
|
|
48
|
+
|
|
49
|
+
# Scan multiple hosts
|
|
50
|
+
postquant scan example.com api.example.com
|
|
51
|
+
|
|
52
|
+
# JSON output
|
|
53
|
+
postquant scan example.com --format json
|
|
54
|
+
|
|
55
|
+
# Read hosts from a file (one per line)
|
|
56
|
+
postquant scan --file hosts.txt
|
|
57
|
+
|
|
58
|
+
# Set connection timeout
|
|
59
|
+
postquant scan example.com --timeout 5000
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Grading
|
|
63
|
+
|
|
64
|
+
| Grade | Meaning |
|
|
65
|
+
|-------|---------|
|
|
66
|
+
| **A+** | All quantum-safe algorithms (PQC key exchange + signatures) |
|
|
67
|
+
| **A** | Quantum-safe with minor observations |
|
|
68
|
+
| **B** | Mostly safe, some moderate-risk items (e.g., AES-128) |
|
|
69
|
+
| **C** | Quantum-vulnerable key exchange or signatures, but TLS 1.3 |
|
|
70
|
+
| **D** | Multiple quantum-vulnerable components |
|
|
71
|
+
| **F** | Critical vulnerabilities + legacy protocols |
|
|
72
|
+
|
|
73
|
+
## Development
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm install # Install dependencies
|
|
77
|
+
npm run build # Compile TypeScript
|
|
78
|
+
npm test # Run tests
|
|
79
|
+
npm run dev -- scan example.com # Run from source
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Roadmap
|
|
83
|
+
|
|
84
|
+
| Phase | Target | Status |
|
|
85
|
+
|-------|--------|--------|
|
|
86
|
+
| TLS scanner CLI | March 2026 | v0.1.0 |
|
|
87
|
+
| Code scanner (Python, JS, Go, Java) | April 2026 | Planned |
|
|
88
|
+
| CBOM generation + risk scoring | May 2026 | Planned |
|
|
89
|
+
| Web dashboard | June 2026 | Planned |
|
|
90
|
+
|
|
91
|
+
See [docs/ROADMAP.md](docs/ROADMAP.md) for details.
|
|
92
|
+
|
|
93
|
+
## Contributing
|
|
94
|
+
|
|
95
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
96
|
+
|
|
97
|
+
## Security
|
|
98
|
+
|
|
99
|
+
See [SECURITY.md](SECURITY.md).
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
[MIT](LICENSE)
|
|
104
|
+
|
|
105
|
+
## Links
|
|
106
|
+
|
|
107
|
+
- [postquant.dev](https://postquant.dev)
|
|
108
|
+
- [@postquantdev](https://x.com/postquantdev)
|
|
109
|
+
- [npm](https://www.npmjs.com/package/postquant)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAgB,WAAW,EAAS,MAAM,mBAAmB,CAAC;AA+B1E,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAwDjB"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { scanHost } from '../scanner/tls.js';
|
|
4
|
+
import { classify } from '../scanner/classifier.js';
|
|
5
|
+
import { grade, shouldFailForGrade } from '../scanner/grader.js';
|
|
6
|
+
import { formatTerminal } from '../output/terminal.js';
|
|
7
|
+
import { formatJson } from '../output/json.js';
|
|
8
|
+
function parseHost(input) {
|
|
9
|
+
const lastColon = input.lastIndexOf(':');
|
|
10
|
+
if (lastColon === -1) {
|
|
11
|
+
return { host: input, port: 443 };
|
|
12
|
+
}
|
|
13
|
+
const portStr = input.slice(lastColon + 1);
|
|
14
|
+
const port = parseInt(portStr, 10);
|
|
15
|
+
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
16
|
+
return { host: input, port: 443 };
|
|
17
|
+
}
|
|
18
|
+
return { host: input.slice(0, lastColon), port };
|
|
19
|
+
}
|
|
20
|
+
function readHostsFile(filePath) {
|
|
21
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
22
|
+
return content
|
|
23
|
+
.split('\n')
|
|
24
|
+
.map((line) => line.trim())
|
|
25
|
+
.filter((line) => line.length > 0 && !line.startsWith('#'));
|
|
26
|
+
}
|
|
27
|
+
export async function scanCommand(hosts, options) {
|
|
28
|
+
const allHostInputs = [...hosts];
|
|
29
|
+
if (options.file) {
|
|
30
|
+
try {
|
|
31
|
+
const fileHosts = readHostsFile(options.file);
|
|
32
|
+
allHostInputs.push(...fileHosts);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
console.error(chalk.red(`Error reading hosts file: ${err.message}`));
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (allHostInputs.length === 0) {
|
|
40
|
+
console.error(chalk.red('No hosts specified. Use: postquant scan <host> or --file <path>'));
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
const results = [];
|
|
44
|
+
let hadErrors = false;
|
|
45
|
+
for (const input of allHostInputs) {
|
|
46
|
+
const { host, port } = parseHost(input);
|
|
47
|
+
try {
|
|
48
|
+
const scanResult = await scanHost(host, port, options.timeout);
|
|
49
|
+
const classified = classify(scanResult);
|
|
50
|
+
const graded = grade(classified);
|
|
51
|
+
results.push(graded);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
hadErrors = true;
|
|
55
|
+
console.error(chalk.red(`\nError scanning ${host}:${port}: ${err.message}`));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (results.length > 0) {
|
|
59
|
+
if (options.format === 'json') {
|
|
60
|
+
console.log(formatJson(results));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
for (const result of results) {
|
|
64
|
+
console.log(formatTerminal(result));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (hadErrors)
|
|
69
|
+
return 1;
|
|
70
|
+
const worstGrade = getWorstGrade(results);
|
|
71
|
+
if (worstGrade && shouldFailForGrade(worstGrade, options.failGrade)) {
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
const GRADE_ORDER = ['A+', 'A', 'B', 'C', 'D', 'F'];
|
|
77
|
+
function getWorstGrade(results) {
|
|
78
|
+
if (results.length === 0)
|
|
79
|
+
return null;
|
|
80
|
+
let worst = 0;
|
|
81
|
+
for (const r of results) {
|
|
82
|
+
const idx = GRADE_ORDER.indexOf(r.grade);
|
|
83
|
+
if (idx > worst)
|
|
84
|
+
worst = idx;
|
|
85
|
+
}
|
|
86
|
+
return GRADE_ORDER[worst];
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { scanCommand } from './commands/scan.js';
|
|
4
|
+
const VALID_GRADES = ['A+', 'A', 'B', 'C', 'D', 'F'];
|
|
5
|
+
const program = new Command();
|
|
6
|
+
program
|
|
7
|
+
.name('postquant')
|
|
8
|
+
.description('Scan TLS endpoints for quantum-vulnerable cryptography')
|
|
9
|
+
.version('0.1.0');
|
|
10
|
+
program
|
|
11
|
+
.command('scan')
|
|
12
|
+
.description('Scan one or more TLS endpoints for quantum readiness')
|
|
13
|
+
.argument('[hosts...]', 'Hostnames to scan (with optional :port)')
|
|
14
|
+
.option('-f, --format <format>', 'Output format (terminal, json)', 'terminal')
|
|
15
|
+
.option('--file <path>', 'Read hosts from file (one per line)')
|
|
16
|
+
.option('--timeout <ms>', 'Connection timeout in milliseconds', '10000')
|
|
17
|
+
.option('--verbose', 'Show raw TLS handshake details', false)
|
|
18
|
+
.option('--fail-grade <grade>', 'Exit non-zero at this grade or worse', 'C')
|
|
19
|
+
.action(async (hosts, opts) => {
|
|
20
|
+
const format = opts.format;
|
|
21
|
+
if (format !== 'terminal' && format !== 'json') {
|
|
22
|
+
console.error(`Invalid format: ${format}. Use 'terminal' or 'json'.`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const failGrade = opts.failGrade;
|
|
26
|
+
if (!VALID_GRADES.includes(failGrade)) {
|
|
27
|
+
console.error(`Invalid fail-grade: ${failGrade}. Use one of: ${VALID_GRADES.join(', ')}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const exitCode = await scanCommand(hosts, {
|
|
31
|
+
format,
|
|
32
|
+
timeout: parseInt(opts.timeout, 10),
|
|
33
|
+
verbose: opts.verbose,
|
|
34
|
+
failGrade,
|
|
35
|
+
file: opts.file,
|
|
36
|
+
});
|
|
37
|
+
process.exit(exitCode);
|
|
38
|
+
});
|
|
39
|
+
program.parse();
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGjD,MAAM,YAAY,GAAY,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAE9D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,wDAAwD,CAAC;KACrE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,sDAAsD,CAAC;KACnE,QAAQ,CAAC,YAAY,EAAE,yCAAyC,CAAC;KACjE,MAAM,CAAC,uBAAuB,EAAE,gCAAgC,EAAE,UAAU,CAAC;KAC7E,MAAM,CAAC,eAAe,EAAE,qCAAqC,CAAC;KAC9D,MAAM,CAAC,gBAAgB,EAAE,oCAAoC,EAAE,OAAO,CAAC;KACvE,MAAM,CAAC,WAAW,EAAE,gCAAgC,EAAE,KAAK,CAAC;KAC5D,MAAM,CACL,sBAAsB,EACtB,sCAAsC,EACtC,GAAG,CACJ;KACA,MAAM,CAAC,KAAK,EAAE,KAAe,EAAE,IAAI,EAAE,EAAE;IACtC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAsB,CAAC;IAC3C,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QAC/C,OAAO,CAAC,KAAK,CAAC,mBAAmB,MAAM,6BAA6B,CAAC,CAAC;QACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAkB,CAAC;IAC1C,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACtC,OAAO,CAAC,KAAK,CACX,uBAAuB,SAAS,iBAAiB,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3E,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE;QACxC,MAAM;QACN,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACnC,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS;QACT,IAAI,EAAE,IAAI,CAAC,IAAI;KAChB,CAAC,CAAC;IAEH,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACzB,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/output/json.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AActD,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAc1D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
function getVersion() {
|
|
5
|
+
try {
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf-8'));
|
|
8
|
+
return pkg.version;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return '0.1.0';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function formatJson(results) {
|
|
15
|
+
const output = {
|
|
16
|
+
version: getVersion(),
|
|
17
|
+
timestamp: new Date().toISOString(),
|
|
18
|
+
results: results.map((r) => ({
|
|
19
|
+
target: `${r.host}:${r.port}`,
|
|
20
|
+
grade: r.grade,
|
|
21
|
+
findings: r.findings,
|
|
22
|
+
summary: r.summary,
|
|
23
|
+
migrationNotes: r.migrationNotes,
|
|
24
|
+
})),
|
|
25
|
+
};
|
|
26
|
+
return JSON.stringify(output, null, 2);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/output/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAG1C,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CACnE,CAAC;QACF,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAuB;IAChD,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,UAAU,EAAE;QACrB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3B,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE;YAC7B,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,cAAc,EAAE,CAAC,CAAC,cAAc;SACjC,CAAC,CAAC;KACJ,CAAC;IAEF,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/output/terminal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,mBAAmB,CAAC;AAwCxE,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAwG3D"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
const VERSION = '0.1.0';
|
|
3
|
+
function riskIcon(risk) {
|
|
4
|
+
switch (risk) {
|
|
5
|
+
case 'critical':
|
|
6
|
+
return chalk.red('🔴 Quantum Vulnerable');
|
|
7
|
+
case 'moderate':
|
|
8
|
+
return chalk.yellow('🟡 Moderate Risk');
|
|
9
|
+
case 'safe':
|
|
10
|
+
return chalk.green('🟢 Quantum Safe');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function protocolLabel(risk) {
|
|
14
|
+
switch (risk) {
|
|
15
|
+
case 'critical':
|
|
16
|
+
return chalk.red('🔴 Legacy (Insecure)');
|
|
17
|
+
case 'moderate':
|
|
18
|
+
return chalk.yellow('🟡 Aging');
|
|
19
|
+
case 'safe':
|
|
20
|
+
return chalk.green('🟢 Current');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function gradeColor(grade) {
|
|
24
|
+
switch (grade) {
|
|
25
|
+
case 'A+':
|
|
26
|
+
case 'A':
|
|
27
|
+
return chalk.green.bold(grade);
|
|
28
|
+
case 'B':
|
|
29
|
+
return chalk.yellow.bold(grade);
|
|
30
|
+
case 'C':
|
|
31
|
+
case 'D':
|
|
32
|
+
case 'F':
|
|
33
|
+
return chalk.red.bold(grade);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function formatTerminal(result) {
|
|
37
|
+
const lines = [];
|
|
38
|
+
const bar = '━'.repeat(48);
|
|
39
|
+
lines.push('');
|
|
40
|
+
lines.push(chalk.bold(`🔐 PostQuant v${VERSION} — Quantum Readiness Scanner`));
|
|
41
|
+
lines.push(chalk.dim(bar));
|
|
42
|
+
lines.push('');
|
|
43
|
+
lines.push(` Target: ${chalk.bold(`${result.host}:${result.port}`)}`);
|
|
44
|
+
lines.push('');
|
|
45
|
+
lines.push(` Overall Grade: ${gradeColor(result.grade)}`);
|
|
46
|
+
lines.push('');
|
|
47
|
+
const certFinding = result.findings.find((f) => f.component === 'certificate');
|
|
48
|
+
if (certFinding) {
|
|
49
|
+
lines.push(' Certificate');
|
|
50
|
+
const algoStr = certFinding.curve
|
|
51
|
+
? `${certFinding.algorithm} ${certFinding.curve}`
|
|
52
|
+
: certFinding.keySize
|
|
53
|
+
? `${certFinding.algorithm}-${certFinding.keySize}`
|
|
54
|
+
: certFinding.algorithm;
|
|
55
|
+
lines.push(` Algorithm: ${algoStr.padEnd(20)} ${riskIcon(certFinding.risk)}`);
|
|
56
|
+
}
|
|
57
|
+
const protocolFinding = result.findings.find((f) => f.component === 'protocol');
|
|
58
|
+
const kxFinding = result.findings.find((f) => f.component === 'keyExchange');
|
|
59
|
+
const cipherFinding = result.findings.find((f) => f.component === 'cipher');
|
|
60
|
+
const hashFinding = result.findings.find((f) => f.component === 'hash');
|
|
61
|
+
lines.push('');
|
|
62
|
+
lines.push(' Connection');
|
|
63
|
+
if (protocolFinding) {
|
|
64
|
+
lines.push(` Protocol: ${protocolFinding.algorithm.padEnd(20)} ${protocolLabel(protocolFinding.risk)}`);
|
|
65
|
+
}
|
|
66
|
+
if (kxFinding) {
|
|
67
|
+
lines.push(` Key Exchange: ${kxFinding.algorithm.padEnd(20)} ${riskIcon(kxFinding.risk)}`);
|
|
68
|
+
}
|
|
69
|
+
if (cipherFinding) {
|
|
70
|
+
lines.push(` Cipher: ${cipherFinding.algorithm.padEnd(20)} ${riskIcon(cipherFinding.risk)}`);
|
|
71
|
+
}
|
|
72
|
+
if (hashFinding) {
|
|
73
|
+
const hashLabel = cipherFinding?.algorithm.includes('GCM') ||
|
|
74
|
+
cipherFinding?.algorithm.includes('CHACHA')
|
|
75
|
+
? 'AEAD'
|
|
76
|
+
: hashFinding.algorithm;
|
|
77
|
+
lines.push(` MAC: ${hashLabel.padEnd(20)} ${riskIcon(hashFinding.risk)}`);
|
|
78
|
+
}
|
|
79
|
+
lines.push('');
|
|
80
|
+
lines.push(' Summary');
|
|
81
|
+
if (result.summary.critical > 0) {
|
|
82
|
+
lines.push(chalk.red(` 🔴 ${result.summary.critical} quantum-vulnerable finding${result.summary.critical > 1 ? 's' : ''}`));
|
|
83
|
+
}
|
|
84
|
+
if (result.summary.moderate > 0) {
|
|
85
|
+
lines.push(chalk.yellow(` 🟡 ${result.summary.moderate} moderate-risk finding${result.summary.moderate > 1 ? 's' : ''}`));
|
|
86
|
+
}
|
|
87
|
+
if (result.summary.safe > 0) {
|
|
88
|
+
lines.push(chalk.green(` 🟢 ${result.summary.safe} quantum-safe finding${result.summary.safe > 1 ? 's' : ''}`));
|
|
89
|
+
}
|
|
90
|
+
if (result.migrationNotes.length > 0) {
|
|
91
|
+
lines.push('');
|
|
92
|
+
lines.push(' Migration Notes');
|
|
93
|
+
for (const note of result.migrationNotes) {
|
|
94
|
+
lines.push(` • ${note}`);
|
|
95
|
+
}
|
|
96
|
+
lines.push(chalk.dim(' • NIST Timeline: Current algorithms deprecated 2030, disallowed 2035'));
|
|
97
|
+
}
|
|
98
|
+
lines.push('');
|
|
99
|
+
lines.push(chalk.dim(bar));
|
|
100
|
+
lines.push('');
|
|
101
|
+
return lines.join('\n');
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/output/terminal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifier.d.ts","sourceRoot":"","sources":["../../src/scanner/classifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAEb,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B,wBAAgB,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,gBAAgB,CAY9D"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
export function classify(scan) {
|
|
2
|
+
return {
|
|
3
|
+
host: scan.host,
|
|
4
|
+
port: scan.port,
|
|
5
|
+
findings: [
|
|
6
|
+
classifyProtocol(scan),
|
|
7
|
+
classifyCertificate(scan),
|
|
8
|
+
classifyKeyExchange(scan),
|
|
9
|
+
classifyCipher(scan),
|
|
10
|
+
classifyHash(scan),
|
|
11
|
+
],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function classifyProtocol(scan) {
|
|
15
|
+
const protocol = scan.protocol ?? '';
|
|
16
|
+
if (protocol === 'TLSv1.3') {
|
|
17
|
+
return {
|
|
18
|
+
component: 'protocol',
|
|
19
|
+
algorithm: 'TLS 1.3',
|
|
20
|
+
risk: 'safe',
|
|
21
|
+
reason: 'Current protocol version',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (protocol === 'TLSv1.2') {
|
|
25
|
+
return {
|
|
26
|
+
component: 'protocol',
|
|
27
|
+
algorithm: 'TLS 1.2',
|
|
28
|
+
risk: 'moderate',
|
|
29
|
+
reason: 'Aging protocol, still functional',
|
|
30
|
+
migration: 'Upgrade to TLS 1.3',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const version = protocol || 'Unknown';
|
|
34
|
+
return {
|
|
35
|
+
component: 'protocol',
|
|
36
|
+
algorithm: version,
|
|
37
|
+
risk: 'critical',
|
|
38
|
+
reason: 'Legacy protocol, already insecure',
|
|
39
|
+
migration: 'Upgrade to TLS 1.3 immediately',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function classifyCertificate(scan) {
|
|
43
|
+
if (!scan.certificate) {
|
|
44
|
+
return {
|
|
45
|
+
component: 'certificate',
|
|
46
|
+
algorithm: 'Unknown',
|
|
47
|
+
risk: 'critical',
|
|
48
|
+
reason: 'Unknown algorithm — assumed vulnerable',
|
|
49
|
+
migration: 'ML-DSA (FIPS 204)',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const algo = scan.certificate.publicKeyAlgorithm.toUpperCase();
|
|
53
|
+
const keySize = scan.certificate.publicKeySize;
|
|
54
|
+
const pqcAlgos = ['ML-DSA', 'ML-KEM', 'SLH-DSA', 'HQC'];
|
|
55
|
+
if (pqcAlgos.some((pqc) => algo.includes(pqc.toUpperCase()))) {
|
|
56
|
+
return {
|
|
57
|
+
component: 'certificate',
|
|
58
|
+
algorithm: scan.certificate.publicKeyAlgorithm,
|
|
59
|
+
keySize,
|
|
60
|
+
risk: 'safe',
|
|
61
|
+
reason: 'NIST post-quantum standard',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const classicalAlgos = ['RSA', 'EC', 'ECDSA', 'ED25519', 'ED448', 'DSA', 'DH'];
|
|
65
|
+
if (classicalAlgos.some((ca) => algo.includes(ca))) {
|
|
66
|
+
return {
|
|
67
|
+
component: 'certificate',
|
|
68
|
+
algorithm: scan.certificate.publicKeyAlgorithm,
|
|
69
|
+
keySize,
|
|
70
|
+
curve: scan.certificate.curve,
|
|
71
|
+
risk: 'critical',
|
|
72
|
+
reason: "Vulnerable to Shor's algorithm",
|
|
73
|
+
migration: 'ML-DSA (FIPS 204)',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
component: 'certificate',
|
|
78
|
+
algorithm: scan.certificate.publicKeyAlgorithm,
|
|
79
|
+
keySize,
|
|
80
|
+
risk: 'critical',
|
|
81
|
+
reason: 'Unknown algorithm — assumed vulnerable',
|
|
82
|
+
migration: 'ML-DSA (FIPS 204)',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function isPqcKeyExchange(scan) {
|
|
86
|
+
const ephName = scan.ephemeralKeyInfo?.name?.toUpperCase() ?? '';
|
|
87
|
+
const cipherName = scan.cipher?.name?.toUpperCase() ?? '';
|
|
88
|
+
const pqcPatterns = ['KYBER', 'MLKEM', 'ML-KEM'];
|
|
89
|
+
return pqcPatterns.some((p) => ephName.includes(p) || cipherName.includes(p));
|
|
90
|
+
}
|
|
91
|
+
function classifyKeyExchange(scan) {
|
|
92
|
+
if (isPqcKeyExchange(scan)) {
|
|
93
|
+
const name = scan.ephemeralKeyInfo?.name ?? 'PQC hybrid';
|
|
94
|
+
return {
|
|
95
|
+
component: 'keyExchange',
|
|
96
|
+
algorithm: name,
|
|
97
|
+
risk: 'safe',
|
|
98
|
+
reason: 'Post-quantum hybrid key exchange',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (!scan.ephemeralKeyInfo) {
|
|
102
|
+
const cipherName = scan.cipher?.name?.toUpperCase() ?? '';
|
|
103
|
+
// TLS 1.2 cipher names include key exchange (e.g., ECDHE-RSA-AES256-GCM-SHA384)
|
|
104
|
+
if (cipherName.includes('ECDHE') || cipherName.includes('X25519')) {
|
|
105
|
+
return {
|
|
106
|
+
component: 'keyExchange',
|
|
107
|
+
algorithm: 'ECDHE (inferred)',
|
|
108
|
+
risk: 'critical',
|
|
109
|
+
reason: "Vulnerable to Shor's algorithm",
|
|
110
|
+
migration: 'ML-KEM (FIPS 203) hybrid key exchange',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (cipherName.includes('DHE')) {
|
|
114
|
+
return {
|
|
115
|
+
component: 'keyExchange',
|
|
116
|
+
algorithm: 'DHE (inferred)',
|
|
117
|
+
risk: 'critical',
|
|
118
|
+
reason: "Vulnerable to Shor's algorithm",
|
|
119
|
+
migration: 'ML-KEM (FIPS 203) hybrid key exchange',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// TLS 1.3 always uses ephemeral key exchange (typically X25519 or ECDHE P-256)
|
|
123
|
+
// but cipher names don't include it — infer from protocol
|
|
124
|
+
if (scan.protocol === 'TLSv1.3') {
|
|
125
|
+
return {
|
|
126
|
+
component: 'keyExchange',
|
|
127
|
+
algorithm: 'X25519 (inferred)',
|
|
128
|
+
risk: 'critical',
|
|
129
|
+
reason: "Vulnerable to Shor's algorithm (TLS 1.3 uses ephemeral ECDHE)",
|
|
130
|
+
migration: 'ML-KEM (FIPS 203) hybrid key exchange',
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
component: 'keyExchange',
|
|
135
|
+
algorithm: 'Unknown',
|
|
136
|
+
risk: 'critical',
|
|
137
|
+
reason: 'Unknown key exchange — assumed vulnerable',
|
|
138
|
+
migration: 'ML-KEM (FIPS 203) hybrid key exchange',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const type = scan.ephemeralKeyInfo.type.toUpperCase();
|
|
142
|
+
const name = scan.ephemeralKeyInfo.name ?? scan.ephemeralKeyInfo.type;
|
|
143
|
+
if (type === 'ECDH' || type === 'DH') {
|
|
144
|
+
return {
|
|
145
|
+
component: 'keyExchange',
|
|
146
|
+
algorithm: name,
|
|
147
|
+
keySize: scan.ephemeralKeyInfo.size,
|
|
148
|
+
risk: 'critical',
|
|
149
|
+
reason: "Vulnerable to Shor's algorithm",
|
|
150
|
+
migration: 'ML-KEM (FIPS 203) hybrid key exchange',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
component: 'keyExchange',
|
|
155
|
+
algorithm: name,
|
|
156
|
+
keySize: scan.ephemeralKeyInfo.size,
|
|
157
|
+
risk: 'critical',
|
|
158
|
+
reason: 'Unknown key exchange — assumed vulnerable',
|
|
159
|
+
migration: 'ML-KEM (FIPS 203) hybrid key exchange',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function classifyCipher(scan) {
|
|
163
|
+
if (!scan.cipher) {
|
|
164
|
+
return {
|
|
165
|
+
component: 'cipher',
|
|
166
|
+
algorithm: 'Unknown',
|
|
167
|
+
risk: 'critical',
|
|
168
|
+
reason: 'Unknown cipher — assumed vulnerable',
|
|
169
|
+
migration: 'Use AES-256-GCM or ChaCha20-Poly1305',
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const name = scan.cipher.name.toUpperCase();
|
|
173
|
+
const bits = scan.cipher.bits;
|
|
174
|
+
if (name.includes('CHACHA20')) {
|
|
175
|
+
return {
|
|
176
|
+
component: 'cipher',
|
|
177
|
+
algorithm: 'ChaCha20-Poly1305',
|
|
178
|
+
keySize: 256,
|
|
179
|
+
risk: 'safe',
|
|
180
|
+
reason: 'Quantum-resistant symmetric cipher',
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (name.includes('AES')) {
|
|
184
|
+
if (bits >= 256) {
|
|
185
|
+
return {
|
|
186
|
+
component: 'cipher',
|
|
187
|
+
algorithm: `AES-${bits}`,
|
|
188
|
+
keySize: bits,
|
|
189
|
+
risk: 'safe',
|
|
190
|
+
reason: 'Quantum-resistant at current key size',
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
component: 'cipher',
|
|
195
|
+
algorithm: `AES-${bits}`,
|
|
196
|
+
keySize: bits,
|
|
197
|
+
risk: 'moderate',
|
|
198
|
+
reason: "Grover's algorithm reduces to 64-bit effective security",
|
|
199
|
+
migration: 'Upgrade to AES-256',
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
component: 'cipher',
|
|
204
|
+
algorithm: scan.cipher.name,
|
|
205
|
+
keySize: bits,
|
|
206
|
+
risk: 'critical',
|
|
207
|
+
reason: 'Unknown cipher — assumed vulnerable',
|
|
208
|
+
migration: 'Use AES-256-GCM or ChaCha20-Poly1305',
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function extractHash(scan) {
|
|
212
|
+
const name = scan.cipher?.name?.toUpperCase() ?? '';
|
|
213
|
+
const standardName = scan.cipher?.standardName?.toUpperCase() ?? '';
|
|
214
|
+
// Check cipher name first (primary source)
|
|
215
|
+
if (name.includes('SHA384') || name.includes('SHA_384'))
|
|
216
|
+
return 'SHA-384';
|
|
217
|
+
if (name.includes('SHA512') || name.includes('SHA_512'))
|
|
218
|
+
return 'SHA-512';
|
|
219
|
+
if (name.includes('SHA256') || name.includes('SHA_256'))
|
|
220
|
+
return 'SHA-256';
|
|
221
|
+
if (name.endsWith('-SHA') || name.endsWith('_SHA'))
|
|
222
|
+
return 'SHA-1';
|
|
223
|
+
if (name.includes('MD5'))
|
|
224
|
+
return 'MD5';
|
|
225
|
+
// Fallback to standardName
|
|
226
|
+
if (standardName.includes('SHA384') || standardName.includes('SHA_384'))
|
|
227
|
+
return 'SHA-384';
|
|
228
|
+
if (standardName.includes('SHA512') || standardName.includes('SHA_512'))
|
|
229
|
+
return 'SHA-512';
|
|
230
|
+
if (standardName.includes('SHA256') || standardName.includes('SHA_256'))
|
|
231
|
+
return 'SHA-256';
|
|
232
|
+
if (standardName.endsWith('_SHA'))
|
|
233
|
+
return 'SHA-1';
|
|
234
|
+
if (standardName.includes('MD5'))
|
|
235
|
+
return 'MD5';
|
|
236
|
+
return 'Unknown';
|
|
237
|
+
}
|
|
238
|
+
function classifyHash(scan) {
|
|
239
|
+
const hash = extractHash(scan);
|
|
240
|
+
if (hash === 'SHA-384' || hash === 'SHA-512') {
|
|
241
|
+
return {
|
|
242
|
+
component: 'hash',
|
|
243
|
+
algorithm: hash,
|
|
244
|
+
risk: 'safe',
|
|
245
|
+
reason: 'Sufficient post-quantum security margin',
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (hash === 'SHA-256') {
|
|
249
|
+
return {
|
|
250
|
+
component: 'hash',
|
|
251
|
+
algorithm: hash,
|
|
252
|
+
risk: 'moderate',
|
|
253
|
+
reason: "Grover's reduces to 128-bit effective (still acceptable)",
|
|
254
|
+
migration: 'Consider upgrading to SHA-384',
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
if (hash === 'SHA-1') {
|
|
258
|
+
return {
|
|
259
|
+
component: 'hash',
|
|
260
|
+
algorithm: hash,
|
|
261
|
+
risk: 'critical',
|
|
262
|
+
reason: 'Already broken — not quantum-specific',
|
|
263
|
+
migration: 'Upgrade to SHA-256 or SHA-384 immediately',
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
if (hash === 'MD5') {
|
|
267
|
+
return {
|
|
268
|
+
component: 'hash',
|
|
269
|
+
algorithm: hash,
|
|
270
|
+
risk: 'critical',
|
|
271
|
+
reason: 'Already broken — not quantum-specific',
|
|
272
|
+
migration: 'Upgrade to SHA-256 or SHA-384 immediately',
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
component: 'hash',
|
|
277
|
+
algorithm: hash,
|
|
278
|
+
risk: 'critical',
|
|
279
|
+
reason: 'Unknown hash — assumed vulnerable',
|
|
280
|
+
migration: 'Use SHA-384 or SHA-512',
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=classifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../src/scanner/classifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ClassifiedResult, GradedResult, Grade } from '../types/index.js';
|
|
2
|
+
export declare function grade(classified: ClassifiedResult): GradedResult;
|
|
3
|
+
export declare function shouldFailForGrade(actual: Grade, threshold: Grade): boolean;
|
|
4
|
+
//# sourceMappingURL=grader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grader.d.ts","sourceRoot":"","sources":["../../src/scanner/grader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACN,MAAM,mBAAmB,CAAC;AAM3B,wBAAgB,KAAK,CAAC,UAAU,EAAE,gBAAgB,GAAG,YAAY,CA4ChE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,OAAO,CAI3E"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const PQC_KEYWORDS = ['KYBER', 'MLKEM', 'ML-KEM', 'ML-DSA', 'SLH-DSA', 'HQC'];
|
|
2
|
+
const GRADE_ORDER = ['A+', 'A', 'B', 'C', 'D', 'F'];
|
|
3
|
+
export function grade(classified) {
|
|
4
|
+
const { findings } = classified;
|
|
5
|
+
const critical = findings.filter((f) => f.risk === 'critical');
|
|
6
|
+
const moderate = findings.filter((f) => f.risk === 'moderate');
|
|
7
|
+
const safe = findings.filter((f) => f.risk === 'safe');
|
|
8
|
+
const protocolFinding = findings.find((f) => f.component === 'protocol');
|
|
9
|
+
const hashFinding = findings.find((f) => f.component === 'hash');
|
|
10
|
+
let computedGrade;
|
|
11
|
+
if (protocolFinding?.risk === 'critical' || hashFinding?.risk === 'critical') {
|
|
12
|
+
computedGrade = 'F';
|
|
13
|
+
}
|
|
14
|
+
else if (critical.length >= 3) {
|
|
15
|
+
computedGrade = 'D';
|
|
16
|
+
}
|
|
17
|
+
else if (critical.length >= 1) {
|
|
18
|
+
computedGrade = 'C';
|
|
19
|
+
}
|
|
20
|
+
else if (moderate.length >= 1) {
|
|
21
|
+
computedGrade = 'B';
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
const hasPqc = findings.some((f) => PQC_KEYWORDS.some((kw) => f.algorithm.toUpperCase().includes(kw)));
|
|
25
|
+
computedGrade = hasPqc ? 'A+' : 'A';
|
|
26
|
+
}
|
|
27
|
+
const migrationNotes = findings
|
|
28
|
+
.filter((f) => f.migration)
|
|
29
|
+
.map((f) => f.migration);
|
|
30
|
+
return {
|
|
31
|
+
host: classified.host,
|
|
32
|
+
port: classified.port,
|
|
33
|
+
grade: computedGrade,
|
|
34
|
+
findings,
|
|
35
|
+
migrationNotes,
|
|
36
|
+
summary: {
|
|
37
|
+
critical: critical.length,
|
|
38
|
+
moderate: moderate.length,
|
|
39
|
+
safe: safe.length,
|
|
40
|
+
total: findings.length,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function shouldFailForGrade(actual, threshold) {
|
|
45
|
+
const actualIndex = GRADE_ORDER.indexOf(actual);
|
|
46
|
+
const thresholdIndex = GRADE_ORDER.indexOf(threshold);
|
|
47
|
+
return actualIndex >= thresholdIndex;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=grader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grader.js","sourceRoot":"","sources":["../../src/scanner/grader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls.d.ts","sourceRoot":"","sources":["../../src/scanner/tls.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,CAAC,CA+BxB"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import tls from 'node:tls';
|
|
2
|
+
export function scanHost(host, port, timeout) {
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
const socket = tls.connect({
|
|
5
|
+
host,
|
|
6
|
+
port,
|
|
7
|
+
rejectUnauthorized: false,
|
|
8
|
+
timeout,
|
|
9
|
+
}, () => {
|
|
10
|
+
try {
|
|
11
|
+
const result = extractTlsData(socket, host, port);
|
|
12
|
+
socket.destroy();
|
|
13
|
+
resolve(result);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
socket.destroy();
|
|
17
|
+
reject(err);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
socket.on('error', (err) => {
|
|
21
|
+
socket.destroy();
|
|
22
|
+
reject(err);
|
|
23
|
+
});
|
|
24
|
+
socket.on('timeout', () => {
|
|
25
|
+
socket.destroy();
|
|
26
|
+
reject(new Error(`Connection to ${host}:${port} timed out`));
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function extractTlsData(socket, host, port) {
|
|
31
|
+
const cipher = socket.getCipher();
|
|
32
|
+
const protocol = socket.getProtocol();
|
|
33
|
+
const cert = socket.getPeerCertificate(true);
|
|
34
|
+
let ephemeralKeyInfo = null;
|
|
35
|
+
try {
|
|
36
|
+
const eki = socket.getEphemeralKeyInfo?.();
|
|
37
|
+
if (eki && eki.type) {
|
|
38
|
+
ephemeralKeyInfo = {
|
|
39
|
+
type: eki.type,
|
|
40
|
+
name: eki.name,
|
|
41
|
+
size: eki.size,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// getEphemeralKeyInfo not available — leave null
|
|
47
|
+
}
|
|
48
|
+
let certificate = null;
|
|
49
|
+
if (cert && cert.subject) {
|
|
50
|
+
const cn = typeof cert.subject === 'object'
|
|
51
|
+
? cert.subject.CN ?? ''
|
|
52
|
+
: String(cert.subject);
|
|
53
|
+
const issuerCN = typeof cert.issuer === 'object'
|
|
54
|
+
? cert.issuer.CN ?? cert.issuer.O ?? ''
|
|
55
|
+
: String(cert.issuer);
|
|
56
|
+
let publicKeyAlgorithm = 'Unknown';
|
|
57
|
+
const publicKeySize = cert.bits ?? 0;
|
|
58
|
+
const curve = cert.asn1Curve;
|
|
59
|
+
const modulus = cert.modulus;
|
|
60
|
+
const sigAlgorithm = cert.sigalg ?? '';
|
|
61
|
+
// Detect algorithm from cert properties (sigalg is not always available)
|
|
62
|
+
if (modulus) {
|
|
63
|
+
publicKeyAlgorithm = 'RSA';
|
|
64
|
+
}
|
|
65
|
+
else if (curve) {
|
|
66
|
+
publicKeyAlgorithm = 'EC';
|
|
67
|
+
}
|
|
68
|
+
else if (sigAlgorithm.includes('RSA') || sigAlgorithm.includes('rsa')) {
|
|
69
|
+
publicKeyAlgorithm = 'RSA';
|
|
70
|
+
}
|
|
71
|
+
else if (sigAlgorithm.includes('ecdsa') || sigAlgorithm.includes('ECDSA')) {
|
|
72
|
+
publicKeyAlgorithm = 'EC';
|
|
73
|
+
}
|
|
74
|
+
else if (sigAlgorithm.includes('ed25519') || sigAlgorithm.includes('Ed25519')) {
|
|
75
|
+
publicKeyAlgorithm = 'Ed25519';
|
|
76
|
+
}
|
|
77
|
+
else if (sigAlgorithm.includes('ed448') || sigAlgorithm.includes('Ed448')) {
|
|
78
|
+
publicKeyAlgorithm = 'Ed448';
|
|
79
|
+
}
|
|
80
|
+
else if (sigAlgorithm.includes('dsa') || sigAlgorithm.includes('DSA')) {
|
|
81
|
+
publicKeyAlgorithm = 'DSA';
|
|
82
|
+
}
|
|
83
|
+
certificate = {
|
|
84
|
+
subject: cn,
|
|
85
|
+
issuer: issuerCN,
|
|
86
|
+
validFrom: cert.valid_from ?? '',
|
|
87
|
+
validTo: cert.valid_to ?? '',
|
|
88
|
+
serialNumber: cert.serialNumber ?? '',
|
|
89
|
+
fingerprint256: cert.fingerprint256 ?? '',
|
|
90
|
+
sigAlgorithm,
|
|
91
|
+
publicKeyAlgorithm,
|
|
92
|
+
publicKeySize,
|
|
93
|
+
curve,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
host,
|
|
98
|
+
port,
|
|
99
|
+
protocol: protocol ?? null,
|
|
100
|
+
cipher: cipher
|
|
101
|
+
? {
|
|
102
|
+
name: cipher.name,
|
|
103
|
+
standardName: cipher.standardName ?? cipher.name,
|
|
104
|
+
version: cipher.version,
|
|
105
|
+
bits: cipher.bits ?? parseCipherBits(cipher.name),
|
|
106
|
+
}
|
|
107
|
+
: null,
|
|
108
|
+
certificate,
|
|
109
|
+
ephemeralKeyInfo,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function parseCipherBits(cipherName) {
|
|
113
|
+
const upper = cipherName.toUpperCase();
|
|
114
|
+
if (upper.includes('256'))
|
|
115
|
+
return 256;
|
|
116
|
+
if (upper.includes('128'))
|
|
117
|
+
return 128;
|
|
118
|
+
if (upper.includes('CHACHA20'))
|
|
119
|
+
return 256;
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=tls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls.js","sourceRoot":"","sources":["../../src/scanner/tls.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export type RiskLevel = 'critical' | 'moderate' | 'safe';
|
|
2
|
+
export type Grade = 'A+' | 'A' | 'B' | 'C' | 'D' | 'F';
|
|
3
|
+
export type ComponentType = 'protocol' | 'certificate' | 'keyExchange' | 'cipher' | 'hash';
|
|
4
|
+
export type OutputFormat = 'terminal' | 'json';
|
|
5
|
+
export interface TlsScanResult {
|
|
6
|
+
host: string;
|
|
7
|
+
port: number;
|
|
8
|
+
protocol: string | null;
|
|
9
|
+
cipher: {
|
|
10
|
+
name: string;
|
|
11
|
+
standardName: string;
|
|
12
|
+
version: string;
|
|
13
|
+
bits: number;
|
|
14
|
+
} | null;
|
|
15
|
+
certificate: {
|
|
16
|
+
subject: string;
|
|
17
|
+
issuer: string;
|
|
18
|
+
validFrom: string;
|
|
19
|
+
validTo: string;
|
|
20
|
+
serialNumber: string;
|
|
21
|
+
fingerprint256: string;
|
|
22
|
+
sigAlgorithm: string;
|
|
23
|
+
publicKeyAlgorithm: string;
|
|
24
|
+
publicKeySize: number;
|
|
25
|
+
curve?: string;
|
|
26
|
+
} | null;
|
|
27
|
+
ephemeralKeyInfo: {
|
|
28
|
+
type: string;
|
|
29
|
+
name?: string;
|
|
30
|
+
size: number;
|
|
31
|
+
} | null;
|
|
32
|
+
}
|
|
33
|
+
export interface ClassifiedFinding {
|
|
34
|
+
component: ComponentType;
|
|
35
|
+
algorithm: string;
|
|
36
|
+
keySize?: number;
|
|
37
|
+
curve?: string;
|
|
38
|
+
risk: RiskLevel;
|
|
39
|
+
reason: string;
|
|
40
|
+
migration?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ClassifiedResult {
|
|
43
|
+
host: string;
|
|
44
|
+
port: number;
|
|
45
|
+
findings: ClassifiedFinding[];
|
|
46
|
+
}
|
|
47
|
+
export interface GradedResult {
|
|
48
|
+
host: string;
|
|
49
|
+
port: number;
|
|
50
|
+
grade: Grade;
|
|
51
|
+
findings: ClassifiedFinding[];
|
|
52
|
+
migrationNotes: string[];
|
|
53
|
+
summary: {
|
|
54
|
+
critical: number;
|
|
55
|
+
moderate: number;
|
|
56
|
+
safe: number;
|
|
57
|
+
total: number;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export interface ScanReport {
|
|
61
|
+
version: string;
|
|
62
|
+
timestamp: string;
|
|
63
|
+
results: GradedResult[];
|
|
64
|
+
}
|
|
65
|
+
export interface ScanOptions {
|
|
66
|
+
format: OutputFormat;
|
|
67
|
+
timeout: number;
|
|
68
|
+
verbose: boolean;
|
|
69
|
+
failGrade: Grade;
|
|
70
|
+
file?: string;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AAEzD,MAAM,MAAM,KAAK,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEvD,MAAM,MAAM,aAAa,GACrB,UAAU,GACV,aAAa,GACb,aAAa,GACb,QAAQ,GACR,MAAM,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;IACT,WAAW,EAAE;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,IAAI,CAAC;IACT,gBAAgB,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,aAAa,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,11 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postquant",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scan your code and infrastructure for quantum-vulnerable cryptography.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"postquant": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest",
|
|
15
|
+
"lint": "tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"post-quantum",
|
|
27
|
+
"cryptography",
|
|
28
|
+
"pqc",
|
|
29
|
+
"security",
|
|
30
|
+
"audit",
|
|
31
|
+
"quantum",
|
|
32
|
+
"nist",
|
|
33
|
+
"ml-kem",
|
|
34
|
+
"ml-dsa",
|
|
35
|
+
"scanner"
|
|
36
|
+
],
|
|
8
37
|
"author": "PostQuant <hello@postquant.dev> (https://postquant.dev)",
|
|
9
38
|
"license": "MIT",
|
|
10
|
-
"homepage": "https://postquant.dev"
|
|
39
|
+
"homepage": "https://postquant.dev",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/postquantdev/postquant.git"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/postquantdev/postquant/issues"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"chalk": "^5.6.2",
|
|
49
|
+
"commander": "^14.0.3"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^25.3.3",
|
|
53
|
+
"tsx": "^4.21.0",
|
|
54
|
+
"typescript": "^5.9.3",
|
|
55
|
+
"vitest": "^4.0.18"
|
|
56
|
+
}
|
|
11
57
|
}
|
package/index.js
DELETED