uneven-ai 1.0.4 → 1.0.6
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/CHANGELOG.md +28 -0
- package/dist/cli/commands/init.js +2 -2
- package/dist/cli/index.js +0 -0
- package/dist/core/active-scanner.d.ts +34 -0
- package/dist/core/active-scanner.d.ts.map +1 -0
- package/dist/core/active-scanner.js +454 -0
- package/dist/core/analyst-job-manager.d.ts +47 -0
- package/dist/core/analyst-job-manager.d.ts.map +1 -0
- package/dist/core/analyst-job-manager.js +96 -0
- package/dist/core/bridge.d.ts +56 -0
- package/dist/core/bridge.d.ts.map +1 -0
- package/dist/core/bridge.js +182 -0
- package/dist/core/chunker.d.ts +30 -0
- package/dist/core/chunker.d.ts.map +1 -0
- package/dist/core/chunker.js +172 -0
- package/dist/core/config-loader.d.ts +27 -0
- package/dist/core/config-loader.d.ts.map +1 -0
- package/dist/core/config-loader.js +75 -0
- package/dist/core/dashboard-generator.d.ts +30 -0
- package/dist/core/dashboard-generator.d.ts.map +1 -0
- package/dist/core/dashboard-generator.js +293 -0
- package/dist/core/data-analyst.d.ts +86 -0
- package/dist/core/data-analyst.d.ts.map +1 -0
- package/dist/core/data-analyst.js +464 -0
- package/dist/core/data-security-context.d.ts +94 -0
- package/dist/core/data-security-context.d.ts.map +1 -0
- package/dist/core/data-security-context.js +253 -0
- package/dist/core/db-loader.d.ts +34 -0
- package/dist/core/db-loader.d.ts.map +1 -0
- package/dist/core/db-loader.js +191 -0
- package/dist/core/engine.d.ts +141 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +1127 -0
- package/dist/core/error-parser.d.ts +42 -0
- package/dist/core/error-parser.d.ts.map +1 -0
- package/dist/core/error-parser.js +477 -0
- package/dist/core/excel-exporter.d.ts +35 -0
- package/dist/core/excel-exporter.d.ts.map +1 -0
- package/dist/core/excel-exporter.js +124 -0
- package/dist/core/external-providers.d.ts +34 -0
- package/dist/core/external-providers.d.ts.map +1 -0
- package/dist/core/external-providers.js +366 -0
- package/dist/core/file-watcher.d.ts +47 -0
- package/dist/core/file-watcher.d.ts.map +1 -0
- package/dist/core/file-watcher.js +125 -0
- package/dist/core/fix-engine.d.ts +112 -0
- package/dist/core/fix-engine.d.ts.map +1 -0
- package/dist/core/fix-engine.js +767 -0
- package/dist/core/git-manager.d.ts +83 -0
- package/dist/core/git-manager.d.ts.map +1 -0
- package/dist/core/git-manager.js +211 -0
- package/dist/core/hardware-detector.d.ts +34 -0
- package/dist/core/hardware-detector.d.ts.map +1 -0
- package/dist/core/hardware-detector.js +76 -0
- package/dist/core/incremental-index.d.ts +79 -0
- package/dist/core/incremental-index.d.ts.map +1 -0
- package/dist/core/incremental-index.js +173 -0
- package/dist/core/index-planner.d.ts +55 -0
- package/dist/core/index-planner.d.ts.map +1 -0
- package/dist/core/index-planner.js +148 -0
- package/dist/core/knowledge-retriever.d.ts +56 -0
- package/dist/core/knowledge-retriever.d.ts.map +1 -0
- package/dist/core/knowledge-retriever.js +146 -0
- package/dist/core/license/fingerprint.d.ts +8 -0
- package/dist/core/license/fingerprint.d.ts.map +1 -0
- package/dist/core/license/fingerprint.js +18 -0
- package/dist/core/license/gate.d.ts +6 -0
- package/dist/core/license/gate.d.ts.map +1 -0
- package/dist/core/license/gate.js +18 -0
- package/dist/core/license/index.d.ts +5 -0
- package/dist/core/license/index.d.ts.map +1 -0
- package/dist/core/license/index.js +4 -0
- package/dist/core/license/manager.d.ts +36 -0
- package/dist/core/license/manager.d.ts.map +1 -0
- package/dist/core/license/manager.js +134 -0
- package/dist/core/license/storage.d.ts +17 -0
- package/dist/core/license/storage.d.ts.map +1 -0
- package/dist/core/license/storage.js +30 -0
- package/dist/core/license/verifier.d.ts +25 -0
- package/dist/core/license/verifier.d.ts.map +1 -0
- package/dist/core/license/verifier.js +33 -0
- package/dist/core/llm-security-reviewer.d.ts +37 -0
- package/dist/core/llm-security-reviewer.d.ts.map +1 -0
- package/dist/core/llm-security-reviewer.js +150 -0
- package/dist/core/logger/index.d.ts +56 -0
- package/dist/core/logger/index.d.ts.map +1 -0
- package/dist/core/logger/index.js +134 -0
- package/dist/core/malware-scanner.d.ts +64 -0
- package/dist/core/malware-scanner.d.ts.map +1 -0
- package/dist/core/malware-scanner.js +526 -0
- package/dist/core/pentest-security-context.d.ts +109 -0
- package/dist/core/pentest-security-context.d.ts.map +1 -0
- package/dist/core/pentest-security-context.js +334 -0
- package/dist/core/process-lock.d.ts +56 -0
- package/dist/core/process-lock.d.ts.map +1 -0
- package/dist/core/process-lock.js +123 -0
- package/dist/core/process-watcher.d.ts +57 -0
- package/dist/core/process-watcher.d.ts.map +1 -0
- package/dist/core/process-watcher.js +158 -0
- package/dist/core/report-packager.d.ts +54 -0
- package/dist/core/report-packager.d.ts.map +1 -0
- package/dist/core/report-packager.js +230 -0
- package/dist/core/resource-guardian.d.ts +39 -0
- package/dist/core/resource-guardian.d.ts.map +1 -0
- package/dist/core/resource-guardian.js +105 -0
- package/dist/core/safety-guard.d.ts +29 -0
- package/dist/core/safety-guard.d.ts.map +1 -0
- package/dist/core/safety-guard.js +79 -0
- package/dist/core/sbom-generator.d.ts +26 -0
- package/dist/core/sbom-generator.d.ts.map +1 -0
- package/dist/core/sbom-generator.js +211 -0
- package/dist/core/security-analyzer.d.ts +37 -0
- package/dist/core/security-analyzer.d.ts.map +1 -0
- package/dist/core/security-analyzer.js +1048 -0
- package/dist/core/security-reporter.d.ts +25 -0
- package/dist/core/security-reporter.d.ts.map +1 -0
- package/dist/core/security-reporter.js +266 -0
- package/dist/core/session.d.ts +150 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +364 -0
- package/dist/core/snapshot.d.ts +65 -0
- package/dist/core/snapshot.d.ts.map +1 -0
- package/dist/core/snapshot.js +188 -0
- package/dist/core/supply-chain-auditor.d.ts +21 -0
- package/dist/core/supply-chain-auditor.d.ts.map +1 -0
- package/dist/core/supply-chain-auditor.js +213 -0
- package/dist/core/test-runner.d.ts +61 -0
- package/dist/core/test-runner.d.ts.map +1 -0
- package/dist/core/test-runner.js +333 -0
- package/dist/core/timeout.d.ts +19 -0
- package/dist/core/timeout.d.ts.map +1 -0
- package/dist/core/timeout.js +28 -0
- package/dist/core/web-scraper.d.ts +36 -0
- package/dist/core/web-scraper.d.ts.map +1 -0
- package/dist/core/web-scraper.js +201 -0
- package/dist/domain/entities/session.js +1 -1
- package/dist/domain/services/index-planner.d.ts.map +1 -1
- package/dist/domain/services/index-planner.js +1 -0
- package/dist/infrastructure/utils/config-loader.d.ts +7 -4
- package/dist/infrastructure/utils/config-loader.d.ts.map +1 -1
- package/dist/infrastructure/utils/config-loader.js +227 -4
- package/package.json +1 -1
- package/prebuilds/linux-arm64/uneven_core.node +0 -0
- package/prebuilds/linux-x64/uneven_core.node +0 -0
- package/scripts/postinstall.cjs +1 -1
- package/prebuilds/darwin-arm64/uneven_core.node +0 -0
- package/prebuilds/darwin-x64/uneven_core.node +0 -0
- package/prebuilds/win32-x64/uneven_core.node +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,34 @@ All notable changes to Uneven AI will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.6] - 2026-04-16
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **askf**: parser no longer creates spurious `output.*` files from unlabelled code blocks in LLM explanation sections (Pattern 3)
|
|
13
|
+
- **askf**: `parseFilesWithId` now tolerates optional whitespace/blank line between filename and opening code fence — silent parse failures eliminated
|
|
14
|
+
- **license**: `parseKey` no longer rejects all `UNVN-PRO-*` / `UNVN-TEAM-*` keys as malformed (wrong prefix `TMR` → `UNVN`)
|
|
15
|
+
- **license**: gate message now shows correct command `uneven-ai license activate` (was missing `-ai`)
|
|
16
|
+
- **analyze**: `--package-exe` flag now works in batch mode — packager was instantiated but never called
|
|
17
|
+
- **packager**: replaced `execFile` + `stdio: inherit` (unsupported) with `spawn` for real-time pkg output streaming
|
|
18
|
+
- **dashboard**: chart gridline colors corrected for light theme (`#1e293b` → `#e2e8f0`)
|
|
19
|
+
- **engine**: `readExcelFile` row cap guard moved before `rows.push` (was accumulating rows past the 500-row limit)
|
|
20
|
+
- **engine**: formula cells now resolved via `v.result` instead of rendering as `[object Object]`
|
|
21
|
+
- **engine**: `.xls` (legacy BIFF format) now detected early with a clear warning instead of silently returning empty string
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **test**: unit tests for `isValidTarget` — IPv4, CIDR, hostname, wildcard, localhost, edge cases (12 tests)
|
|
26
|
+
- **test**: unit tests for `parseFiles` and `parseFilesWithId` — all patterns including spurious-file regression (11 tests)
|
|
27
|
+
|
|
28
|
+
## [1.0.5] - 2026-04-16
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **config**: `uneven.config.ts` changes now applied automatically on every command — no longer requires manual edit of `.uneven/config.json` or re-running `uneven init`
|
|
33
|
+
- **init**: default Gemini model changed from `gemini-2.0-flash` to `gemini-2.5-flash`
|
|
34
|
+
- **index-planner**: added `gemini-2.5-flash` pricing entry to cost estimation table
|
|
35
|
+
|
|
8
36
|
## [1.0.4] - 2026-04-16
|
|
9
37
|
|
|
10
38
|
### Fixed
|
|
@@ -48,9 +48,9 @@ function defaultModelFor(provider) {
|
|
|
48
48
|
ollama: 'llama3.2',
|
|
49
49
|
claude: 'claude-sonnet-4-6',
|
|
50
50
|
openai: 'gpt-4o-mini',
|
|
51
|
-
gemini: 'gemini-2.
|
|
51
|
+
gemini: 'gemini-2.5-flash',
|
|
52
52
|
};
|
|
53
|
-
return MAP[provider] ?? 'gemini-2.
|
|
53
|
+
return MAP[provider] ?? 'gemini-2.5-flash';
|
|
54
54
|
}
|
|
55
55
|
function makeConfig(provider, threads, gpuLayers) {
|
|
56
56
|
const model = defaultModelFor(provider);
|
package/dist/cli/index.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active Scanner — Phase 3
|
|
3
|
+
*
|
|
4
|
+
* Real network-based probes against authorized targets only.
|
|
5
|
+
* Uses only Node.js built-ins (net, tls, https, http) — zero extra deps.
|
|
6
|
+
*
|
|
7
|
+
* Checks:
|
|
8
|
+
* 1. Port scan — TCP connect scan on common/dangerous ports
|
|
9
|
+
* 2. HTTP header audit — missing security headers, server version disclosure
|
|
10
|
+
* 3. SSL/TLS audit — expired cert, TLS 1.0/1.1, self-signed, weak cipher
|
|
11
|
+
* 4. CORS reflection — tests if server reflects arbitrary Origin header
|
|
12
|
+
* 5. HTTP methods — checks for dangerous methods (PUT, DELETE, TRACE)
|
|
13
|
+
*
|
|
14
|
+
* Every probe is guarded by PentestSecurityContext.checkTarget() before execution.
|
|
15
|
+
* All results are written to .uneven/pentest-audit.log.
|
|
16
|
+
*/
|
|
17
|
+
import { Logger } from './logger/index.js';
|
|
18
|
+
import { PentestSecurityContext } from './pentest-security-context.js';
|
|
19
|
+
import { type SecurityFinding } from './security-analyzer.js';
|
|
20
|
+
export declare class ActiveScanner {
|
|
21
|
+
private logger;
|
|
22
|
+
private ctx;
|
|
23
|
+
private auditLog;
|
|
24
|
+
constructor(logger: Logger, ctx: PentestSecurityContext);
|
|
25
|
+
scanPorts(target: string): Promise<SecurityFinding[]>;
|
|
26
|
+
auditHeaders(target: string): Promise<SecurityFinding[]>;
|
|
27
|
+
auditTLS(target: string): Promise<SecurityFinding[]>;
|
|
28
|
+
testCORS(target: string): Promise<SecurityFinding[]>;
|
|
29
|
+
testDangerousMethods(target: string): Promise<SecurityFinding[]>;
|
|
30
|
+
runAgainstTarget(target: string): Promise<SecurityFinding[]>;
|
|
31
|
+
private audit;
|
|
32
|
+
private flushAuditLog;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=active-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-scanner.d.ts","sourceRoot":"","sources":["../../src/core/active-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAyI7D,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,GAAG,CAAwB;IACnC,OAAO,CAAC,QAAQ,CAAe;gBAEnB,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,sBAAsB;IAOjD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA8CrD,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA8GxD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA4EpD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAoEpD,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA0ChE,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAwBlE,OAAO,CAAC,KAAK;YAKC,aAAa;CAQ5B"}
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active Scanner — Phase 3
|
|
3
|
+
*
|
|
4
|
+
* Real network-based probes against authorized targets only.
|
|
5
|
+
* Uses only Node.js built-ins (net, tls, https, http) — zero extra deps.
|
|
6
|
+
*
|
|
7
|
+
* Checks:
|
|
8
|
+
* 1. Port scan — TCP connect scan on common/dangerous ports
|
|
9
|
+
* 2. HTTP header audit — missing security headers, server version disclosure
|
|
10
|
+
* 3. SSL/TLS audit — expired cert, TLS 1.0/1.1, self-signed, weak cipher
|
|
11
|
+
* 4. CORS reflection — tests if server reflects arbitrary Origin header
|
|
12
|
+
* 5. HTTP methods — checks for dangerous methods (PUT, DELETE, TRACE)
|
|
13
|
+
*
|
|
14
|
+
* Every probe is guarded by PentestSecurityContext.checkTarget() before execution.
|
|
15
|
+
* All results are written to .uneven/pentest-audit.log.
|
|
16
|
+
*/
|
|
17
|
+
import * as net from 'net';
|
|
18
|
+
import * as tls from 'tls';
|
|
19
|
+
import * as https from 'https';
|
|
20
|
+
import * as http from 'http';
|
|
21
|
+
import * as fs from 'fs/promises';
|
|
22
|
+
import * as path from 'path';
|
|
23
|
+
// ─── Timeouts ─────────────────────────────────────────────────────────────────
|
|
24
|
+
const CONNECT_TIMEOUT_MS = 3000;
|
|
25
|
+
const HTTP_TIMEOUT_MS = 8000;
|
|
26
|
+
const PORTS = [
|
|
27
|
+
{ port: 21, service: 'FTP', dangerous: true, severity: 'high', cvss: 7.5, message: 'FTP (port 21) is open — transmits credentials in plain text', recommendation: 'Disable FTP. Use SFTP (port 22) instead.' },
|
|
28
|
+
{ port: 22, service: 'SSH', dangerous: false, severity: 'info', cvss: 0, message: 'SSH (port 22) open', recommendation: 'Ensure SSH uses key authentication and has fail2ban/rate limiting.' },
|
|
29
|
+
{ port: 23, service: 'Telnet', dangerous: true, severity: 'critical', cvss: 9.8, message: 'Telnet (port 23) is open — unencrypted remote access', recommendation: 'Disable Telnet immediately. Use SSH.' },
|
|
30
|
+
{ port: 25, service: 'SMTP', dangerous: false, severity: 'info', cvss: 0, message: 'SMTP (port 25) open', recommendation: 'Ensure SMTP relay is restricted. Enable STARTTLS.' },
|
|
31
|
+
{ port: 80, service: 'HTTP', dangerous: false, severity: 'info', cvss: 0, message: 'HTTP (port 80) open', recommendation: 'Redirect all HTTP to HTTPS.' },
|
|
32
|
+
{ port: 443, service: 'HTTPS', dangerous: false, severity: 'info', cvss: 0, message: 'HTTPS (port 443) open', recommendation: 'Verify TLS configuration.' },
|
|
33
|
+
{ port: 3306, service: 'MySQL', dangerous: true, severity: 'critical', cvss: 9.8, message: 'MySQL (port 3306) exposed to network — database should not be publicly accessible', recommendation: 'Bind MySQL to 127.0.0.1. Use a firewall to block external access.' },
|
|
34
|
+
{ port: 5432, service: 'PostgreSQL', dangerous: true, severity: 'critical', cvss: 9.8, message: 'PostgreSQL (port 5432) exposed to network', recommendation: 'Bind PostgreSQL to 127.0.0.1. Use a firewall.' },
|
|
35
|
+
{ port: 6379, service: 'Redis', dangerous: true, severity: 'critical', cvss: 10.0, message: 'Redis (port 6379) exposed — often has no authentication by default', recommendation: 'Bind to 127.0.0.1, enable requirepass, use TLS.' },
|
|
36
|
+
{ port: 27017, service: 'MongoDB', dangerous: true, severity: 'critical', cvss: 10.0, message: 'MongoDB (port 27017) exposed — may allow unauthenticated access', recommendation: 'Enable authentication, bind to localhost, restrict with firewall.' },
|
|
37
|
+
{ port: 5984, service: 'CouchDB', dangerous: true, severity: 'critical', cvss: 9.8, message: 'CouchDB (port 5984) exposed — web-accessible database', recommendation: 'Restrict CouchDB to localhost. Enable authentication.' },
|
|
38
|
+
{ port: 9200, service: 'Elasticsearch', dangerous: true, severity: 'critical', cvss: 10.0, message: 'Elasticsearch (port 9200) exposed — no authentication by default in older versions', recommendation: 'Enable X-Pack security, restrict access with firewall.' },
|
|
39
|
+
{ port: 8080, service: 'HTTP-alt', dangerous: false, severity: 'low', cvss: 3.1, message: 'Alternate HTTP port 8080 open — may expose admin panels or dev servers', recommendation: 'Verify what service is running. Restrict if not needed publicly.' },
|
|
40
|
+
{ port: 8443, service: 'HTTPS-alt', dangerous: false, severity: 'info', cvss: 0, message: 'Alternate HTTPS port 8443 open', recommendation: 'Verify what service is running.' },
|
|
41
|
+
{ port: 4444, service: 'Metasploit', dangerous: true, severity: 'critical', cvss: 10.0, message: 'Port 4444 open — common Metasploit handler / backdoor port', recommendation: 'Investigate immediately. Close if unauthorized.' },
|
|
42
|
+
{ port: 1433, service: 'MSSQL', dangerous: true, severity: 'critical', cvss: 9.8, message: 'MSSQL (port 1433) exposed to network', recommendation: 'Restrict MSSQL to internal network only. Enable Windows Authentication.' },
|
|
43
|
+
{ port: 2375, service: 'Docker', dangerous: true, severity: 'critical', cvss: 10.0, message: 'Docker API (port 2375) exposed without TLS — full container control possible', recommendation: 'Never expose Docker API without TLS. Use unix socket instead.' },
|
|
44
|
+
{ port: 9000, service: 'Portainer/PHP-FPM', dangerous: true, severity: 'high', cvss: 8.6, message: 'Port 9000 open — Portainer admin or PHP-FPM may be exposed', recommendation: 'Restrict to internal network. Enable authentication.' },
|
|
45
|
+
];
|
|
46
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
47
|
+
function tcpConnect(host, port, timeoutMs) {
|
|
48
|
+
return new Promise(resolve => {
|
|
49
|
+
const sock = net.createConnection({ host, port });
|
|
50
|
+
const timer = setTimeout(() => { sock.destroy(); resolve(false); }, timeoutMs);
|
|
51
|
+
sock.on('connect', () => { clearTimeout(timer); sock.destroy(); resolve(true); });
|
|
52
|
+
sock.on('error', () => { clearTimeout(timer); resolve(false); });
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function httpRequest(options, useHttps) {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
const mod = useHttps ? https : http;
|
|
58
|
+
const req = mod.request({ ...options, rejectUnauthorized: false }, res => {
|
|
59
|
+
let body = '';
|
|
60
|
+
res.on('data', (chunk) => { body += chunk.toString('utf-8'); });
|
|
61
|
+
res.on('end', () => resolve({
|
|
62
|
+
statusCode: res.statusCode ?? 0,
|
|
63
|
+
headers: res.headers,
|
|
64
|
+
body: body.slice(0, 4096),
|
|
65
|
+
}));
|
|
66
|
+
});
|
|
67
|
+
req.setTimeout(HTTP_TIMEOUT_MS, () => { req.destroy(); reject(new Error('timeout')); });
|
|
68
|
+
req.on('error', reject);
|
|
69
|
+
req.end();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function parseTlsInfo(host, port) {
|
|
73
|
+
return new Promise(resolve => {
|
|
74
|
+
const sock = tls.connect({ host, port, rejectUnauthorized: false, timeout: CONNECT_TIMEOUT_MS }, () => {
|
|
75
|
+
const cert = sock.getPeerCertificate();
|
|
76
|
+
const protocol = sock.getProtocol() ?? 'unknown';
|
|
77
|
+
if (!cert || !cert.valid_to) {
|
|
78
|
+
sock.destroy();
|
|
79
|
+
resolve(null);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const expiry = new Date(cert.valid_to);
|
|
83
|
+
const now = new Date();
|
|
84
|
+
const daysToExpiry = Math.floor((expiry.getTime() - now.getTime()) / 86400000);
|
|
85
|
+
const selfSigned = cert.issuer?.CN === cert.subject?.CN;
|
|
86
|
+
resolve({
|
|
87
|
+
valid: sock.authorized,
|
|
88
|
+
expired: daysToExpiry < 0,
|
|
89
|
+
selfSigned,
|
|
90
|
+
daysToExpiry,
|
|
91
|
+
protocol,
|
|
92
|
+
subject: String(cert.subject?.CN ?? ''),
|
|
93
|
+
issuer: String(cert.issuer?.CN ?? ''),
|
|
94
|
+
});
|
|
95
|
+
sock.destroy();
|
|
96
|
+
});
|
|
97
|
+
sock.setTimeout(CONNECT_TIMEOUT_MS, () => { sock.destroy(); resolve(null); });
|
|
98
|
+
sock.on('error', () => resolve(null));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function resolveHostPort(target) {
|
|
102
|
+
// Handles: example.com, 192.168.1.1, https://example.com, example.com:8443
|
|
103
|
+
let raw = target;
|
|
104
|
+
let useHttps = true;
|
|
105
|
+
let defaultPort = 443;
|
|
106
|
+
if (raw.startsWith('http://')) {
|
|
107
|
+
raw = raw.slice(7);
|
|
108
|
+
useHttps = false;
|
|
109
|
+
defaultPort = 80;
|
|
110
|
+
}
|
|
111
|
+
else if (raw.startsWith('https://')) {
|
|
112
|
+
raw = raw.slice(8);
|
|
113
|
+
}
|
|
114
|
+
const [host, portStr] = raw.split(':');
|
|
115
|
+
const port = portStr ? parseInt(portStr, 10) : defaultPort;
|
|
116
|
+
return { host: host ?? raw, port, https: useHttps };
|
|
117
|
+
}
|
|
118
|
+
// ─── Main class ───────────────────────────────────────────────────────────────
|
|
119
|
+
export class ActiveScanner {
|
|
120
|
+
logger;
|
|
121
|
+
ctx;
|
|
122
|
+
auditLog = [];
|
|
123
|
+
constructor(logger, ctx) {
|
|
124
|
+
this.logger = logger;
|
|
125
|
+
this.ctx = ctx;
|
|
126
|
+
}
|
|
127
|
+
// ── 1. Port scan ─────────────────────────────────────────────────────────────
|
|
128
|
+
async scanPorts(target) {
|
|
129
|
+
const { host } = resolveHostPort(target);
|
|
130
|
+
await this.logger.info(`ActiveScan: port scan → ${host}`);
|
|
131
|
+
const findings = [];
|
|
132
|
+
const openPorts = [];
|
|
133
|
+
await Promise.all(PORTS.map(async (def) => {
|
|
134
|
+
const check = await this.ctx.checkTarget(`${host}:${def.port}`);
|
|
135
|
+
if (check.blocked) {
|
|
136
|
+
this.audit(`BLOCKED port scan ${host}:${def.port} — ${check.reason}`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const open = await tcpConnect(host, def.port, CONNECT_TIMEOUT_MS);
|
|
140
|
+
this.audit(`port ${host}:${def.port} (${def.service}) — ${open ? 'OPEN' : 'closed'}`);
|
|
141
|
+
if (!open)
|
|
142
|
+
return;
|
|
143
|
+
openPorts.push(def.port);
|
|
144
|
+
if (def.dangerous) {
|
|
145
|
+
findings.push({
|
|
146
|
+
severity: def.severity,
|
|
147
|
+
type: `ActiveScan: Exposed Port (${def.service})`,
|
|
148
|
+
message: def.message,
|
|
149
|
+
recommendation: def.recommendation,
|
|
150
|
+
cvss: def.cvss,
|
|
151
|
+
match: `${host}:${def.port}`,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
else if (def.severity !== 'info') {
|
|
155
|
+
findings.push({
|
|
156
|
+
severity: def.severity,
|
|
157
|
+
type: `ActiveScan: Open Port (${def.service})`,
|
|
158
|
+
message: def.message,
|
|
159
|
+
recommendation: def.recommendation,
|
|
160
|
+
cvss: def.cvss,
|
|
161
|
+
match: `${host}:${def.port}`,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}));
|
|
165
|
+
await this.logger.info(`ActiveScan: ${openPorts.length} open ports on ${host}`);
|
|
166
|
+
return findings;
|
|
167
|
+
}
|
|
168
|
+
// ── 2. HTTP header audit ─────────────────────────────────────────────────────
|
|
169
|
+
async auditHeaders(target) {
|
|
170
|
+
const { host, port, https: useHttps } = resolveHostPort(target);
|
|
171
|
+
await this.logger.info(`ActiveScan: HTTP header audit → ${host}:${port}`);
|
|
172
|
+
const findings = [];
|
|
173
|
+
const check = await this.ctx.checkTarget(target);
|
|
174
|
+
if (check.blocked) {
|
|
175
|
+
this.audit(`BLOCKED header audit ${target} — ${check.reason}`);
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
let res;
|
|
179
|
+
try {
|
|
180
|
+
res = await httpRequest({ host, port, path: '/', method: 'GET' }, useHttps);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
this.audit(`HTTP header audit ${host}:${port} — ${res.statusCode}`);
|
|
186
|
+
const h = res.headers;
|
|
187
|
+
const REQUIRED = [
|
|
188
|
+
{
|
|
189
|
+
header: 'Strict-Transport-Security', key: 'strict-transport-security',
|
|
190
|
+
severity: 'high', cvss: 6.5,
|
|
191
|
+
message: `Missing Strict-Transport-Security (HSTS) on ${host} — users can be downgraded to HTTP`,
|
|
192
|
+
recommendation: "Add: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
header: 'Content-Security-Policy', key: 'content-security-policy',
|
|
196
|
+
severity: 'high', cvss: 6.1,
|
|
197
|
+
message: `Missing Content-Security-Policy on ${host} — XSS has no browser-level mitigation`,
|
|
198
|
+
recommendation: "Define a CSP. Minimum: Content-Security-Policy: default-src 'self'",
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
header: 'X-Content-Type-Options', key: 'x-content-type-options',
|
|
202
|
+
severity: 'medium', cvss: 4.3,
|
|
203
|
+
message: `Missing X-Content-Type-Options: nosniff on ${host}`,
|
|
204
|
+
recommendation: "Add: X-Content-Type-Options: nosniff",
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
header: 'X-Frame-Options', key: 'x-frame-options',
|
|
208
|
+
severity: 'medium', cvss: 4.3,
|
|
209
|
+
message: `Missing X-Frame-Options on ${host} — clickjacking possible`,
|
|
210
|
+
recommendation: "Add: X-Frame-Options: SAMEORIGIN",
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
header: 'Referrer-Policy', key: 'referrer-policy',
|
|
214
|
+
severity: 'low', cvss: 3.1,
|
|
215
|
+
message: `Missing Referrer-Policy on ${host} — referrer URL may leak sensitive paths`,
|
|
216
|
+
recommendation: "Add: Referrer-Policy: strict-origin-when-cross-origin",
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
header: 'Permissions-Policy', key: 'permissions-policy',
|
|
220
|
+
severity: 'low', cvss: 2.6,
|
|
221
|
+
message: `Missing Permissions-Policy on ${host} — browser features not explicitly restricted`,
|
|
222
|
+
recommendation: "Add: Permissions-Policy: camera=(), microphone=(), geolocation=()",
|
|
223
|
+
},
|
|
224
|
+
];
|
|
225
|
+
for (const rule of REQUIRED) {
|
|
226
|
+
if (!h[rule.key]) {
|
|
227
|
+
findings.push({
|
|
228
|
+
severity: rule.severity,
|
|
229
|
+
type: `ActiveScan: Missing ${rule.header}`,
|
|
230
|
+
message: rule.message,
|
|
231
|
+
recommendation: rule.recommendation,
|
|
232
|
+
cvss: rule.cvss,
|
|
233
|
+
match: host,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Server version disclosure
|
|
238
|
+
const server = h['server'];
|
|
239
|
+
if (server && /[\d.]+/.test(server)) {
|
|
240
|
+
findings.push({
|
|
241
|
+
severity: 'low', cvss: 3.7,
|
|
242
|
+
type: 'ActiveScan: Server Version Disclosure',
|
|
243
|
+
message: `Server header reveals version: "${server}" — aids fingerprinting attacks`,
|
|
244
|
+
recommendation: 'Remove version from Server header. Use a generic value or remove it entirely.',
|
|
245
|
+
match: server,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
// X-Powered-By disclosure
|
|
249
|
+
const powered = h['x-powered-by'];
|
|
250
|
+
if (powered) {
|
|
251
|
+
findings.push({
|
|
252
|
+
severity: 'low', cvss: 3.1,
|
|
253
|
+
type: 'ActiveScan: X-Powered-By Disclosure',
|
|
254
|
+
message: `X-Powered-By header reveals technology: "${powered}"`,
|
|
255
|
+
recommendation: "Remove X-Powered-By header: app.disable('x-powered-by') or use helmet().",
|
|
256
|
+
match: powered,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return findings;
|
|
260
|
+
}
|
|
261
|
+
// ── 3. SSL/TLS audit ─────────────────────────────────────────────────────────
|
|
262
|
+
async auditTLS(target) {
|
|
263
|
+
const { host, port } = resolveHostPort(target);
|
|
264
|
+
await this.logger.info(`ActiveScan: TLS audit → ${host}:${port}`);
|
|
265
|
+
const findings = [];
|
|
266
|
+
const check = await this.ctx.checkTarget(target);
|
|
267
|
+
if (check.blocked) {
|
|
268
|
+
this.audit(`BLOCKED TLS audit ${target} — ${check.reason}`);
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
const info = await parseTlsInfo(host, port);
|
|
272
|
+
if (!info) {
|
|
273
|
+
this.audit(`TLS audit ${host}:${port} — no TLS or unreachable`);
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
this.audit(`TLS ${host}:${port} — protocol=${info.protocol} expired=${info.expired} selfSigned=${info.selfSigned} daysLeft=${info.daysToExpiry}`);
|
|
277
|
+
// Expired certificate
|
|
278
|
+
if (info.expired) {
|
|
279
|
+
findings.push({
|
|
280
|
+
severity: 'critical', cvss: 9.8,
|
|
281
|
+
type: 'ActiveScan: Expired TLS Certificate',
|
|
282
|
+
message: `TLS certificate on ${host}:${port} expired ${Math.abs(info.daysToExpiry)} day(s) ago — browsers will block the site`,
|
|
283
|
+
recommendation: 'Renew the certificate immediately. Use Let\'s Encrypt for automatic renewal.',
|
|
284
|
+
match: `${host}:${port}`,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
else if (info.daysToExpiry < 30) {
|
|
288
|
+
findings.push({
|
|
289
|
+
severity: 'high', cvss: 7.5,
|
|
290
|
+
type: 'ActiveScan: TLS Certificate Expiring Soon',
|
|
291
|
+
message: `TLS certificate on ${host}:${port} expires in ${info.daysToExpiry} day(s)`,
|
|
292
|
+
recommendation: 'Renew the certificate before it expires. Set up auto-renewal with certbot.',
|
|
293
|
+
match: `${host}:${port}`,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
// Self-signed certificate
|
|
297
|
+
if (info.selfSigned) {
|
|
298
|
+
findings.push({
|
|
299
|
+
severity: 'high', cvss: 7.4,
|
|
300
|
+
type: 'ActiveScan: Self-Signed Certificate',
|
|
301
|
+
message: `${host}:${port} uses a self-signed certificate — not trusted by browsers, vulnerable to MITM`,
|
|
302
|
+
recommendation: 'Replace with a certificate from a trusted CA. Let\'s Encrypt provides free certificates.',
|
|
303
|
+
match: info.subject,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
// Deprecated TLS version
|
|
307
|
+
if (info.protocol === 'TLSv1' || info.protocol === 'TLSv1.1') {
|
|
308
|
+
findings.push({
|
|
309
|
+
severity: 'high', cvss: 7.4,
|
|
310
|
+
type: `ActiveScan: Deprecated TLS (${info.protocol})`,
|
|
311
|
+
message: `${host}:${port} negotiated ${info.protocol} — deprecated and vulnerable to POODLE/BEAST attacks`,
|
|
312
|
+
recommendation: 'Disable TLS 1.0 and 1.1. Require TLS 1.2 minimum, prefer TLS 1.3.',
|
|
313
|
+
match: info.protocol,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
// No TLS 1.3
|
|
317
|
+
if (info.protocol !== 'TLSv1.3' && !info.expired) {
|
|
318
|
+
findings.push({
|
|
319
|
+
severity: 'info', cvss: 0,
|
|
320
|
+
type: 'ActiveScan: TLS 1.3 Not Negotiated',
|
|
321
|
+
message: `${host}:${port} did not negotiate TLS 1.3 (got ${info.protocol}) — modern clients prefer TLS 1.3`,
|
|
322
|
+
recommendation: 'Enable TLS 1.3 support on the server for better security and performance.',
|
|
323
|
+
match: info.protocol,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
return findings;
|
|
327
|
+
}
|
|
328
|
+
// ── 4. CORS reflection test ───────────────────────────────────────────────────
|
|
329
|
+
async testCORS(target) {
|
|
330
|
+
const { host, port, https: useHttps } = resolveHostPort(target);
|
|
331
|
+
await this.logger.info(`ActiveScan: CORS test → ${host}:${port}`);
|
|
332
|
+
const findings = [];
|
|
333
|
+
const check = await this.ctx.checkTarget(target);
|
|
334
|
+
if (check.blocked) {
|
|
335
|
+
this.audit(`BLOCKED CORS test ${target} — ${check.reason}`);
|
|
336
|
+
return [];
|
|
337
|
+
}
|
|
338
|
+
const EVIL_ORIGIN = 'https://evil.attacker-example.com';
|
|
339
|
+
let res;
|
|
340
|
+
try {
|
|
341
|
+
res = await httpRequest({
|
|
342
|
+
host, port,
|
|
343
|
+
path: '/',
|
|
344
|
+
method: 'GET',
|
|
345
|
+
headers: { Origin: EVIL_ORIGIN },
|
|
346
|
+
}, useHttps);
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
|
+
this.audit(`CORS test ${host}:${port} — ACAO=${res.headers['access-control-allow-origin']}`);
|
|
352
|
+
const acao = res.headers['access-control-allow-origin'];
|
|
353
|
+
const acac = res.headers['access-control-allow-credentials'];
|
|
354
|
+
// Server reflects arbitrary origin
|
|
355
|
+
if (acao === EVIL_ORIGIN) {
|
|
356
|
+
findings.push({
|
|
357
|
+
severity: 'high', cvss: 8.1,
|
|
358
|
+
type: 'ActiveScan: CORS Origin Reflection',
|
|
359
|
+
message: `${host} reflects any Origin header in Access-Control-Allow-Origin — any website can make credentialed cross-origin requests`,
|
|
360
|
+
recommendation: 'Replace origin reflection with an explicit allowlist. Never echo Origin without validation.',
|
|
361
|
+
match: acao,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
// Null origin allowed
|
|
365
|
+
if (acao === 'null') {
|
|
366
|
+
findings.push({
|
|
367
|
+
severity: 'high', cvss: 7.4,
|
|
368
|
+
type: 'ActiveScan: CORS Null Origin Allowed',
|
|
369
|
+
message: `${host} allows Origin: null — can be exploited from sandboxed iframes and local files`,
|
|
370
|
+
recommendation: 'Remove null from the CORS origin allowlist.',
|
|
371
|
+
match: 'null',
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
// Wildcard with credentials
|
|
375
|
+
if (acao === '*' && acac === 'true') {
|
|
376
|
+
findings.push({
|
|
377
|
+
severity: 'critical', cvss: 9.8,
|
|
378
|
+
type: 'ActiveScan: CORS Wildcard + Credentials',
|
|
379
|
+
message: `${host} sends Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true — browsers reject this, but it indicates a server misconfiguration`,
|
|
380
|
+
recommendation: "Replace '*' with an explicit origin. You cannot use wildcard with credentials: true.",
|
|
381
|
+
match: `${acao} + credentials`,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
return findings;
|
|
385
|
+
}
|
|
386
|
+
// ── 5. HTTP dangerous methods ─────────────────────────────────────────────────
|
|
387
|
+
async testDangerousMethods(target) {
|
|
388
|
+
const { host, port, https: useHttps } = resolveHostPort(target);
|
|
389
|
+
await this.logger.info(`ActiveScan: HTTP methods → ${host}:${port}`);
|
|
390
|
+
const findings = [];
|
|
391
|
+
const check = await this.ctx.checkTarget(target);
|
|
392
|
+
if (check.blocked) {
|
|
393
|
+
this.audit(`BLOCKED method test ${target} — ${check.reason}`);
|
|
394
|
+
return [];
|
|
395
|
+
}
|
|
396
|
+
const DANGEROUS = [
|
|
397
|
+
{ method: 'TRACE', severity: 'medium', cvss: 4.8, reason: 'Cross-Site Tracing (XST) — can steal cookies/headers via JavaScript' },
|
|
398
|
+
{ method: 'PUT', severity: 'high', cvss: 7.5, reason: 'PUT method allowed — may permit arbitrary file upload' },
|
|
399
|
+
{ method: 'DELETE', severity: 'high', cvss: 7.5, reason: 'DELETE method allowed — may permit resource deletion' },
|
|
400
|
+
{ method: 'CONNECT', severity: 'medium', cvss: 5.8, reason: 'CONNECT method allowed — server may be used as HTTP proxy' },
|
|
401
|
+
];
|
|
402
|
+
await Promise.all(DANGEROUS.map(async ({ method, severity, cvss, reason }) => {
|
|
403
|
+
try {
|
|
404
|
+
const res = await httpRequest({ host, port, path: '/', method }, useHttps);
|
|
405
|
+
this.audit(`${method} ${host}:${port} — ${res.statusCode}`);
|
|
406
|
+
// 200/204 = method accepted; 405 = properly blocked
|
|
407
|
+
if (res.statusCode !== 405 && res.statusCode !== 501 && res.statusCode !== 0) {
|
|
408
|
+
findings.push({
|
|
409
|
+
severity,
|
|
410
|
+
type: `ActiveScan: HTTP ${method} Method Allowed`,
|
|
411
|
+
message: `${host} accepts HTTP ${method} requests (${res.statusCode}) — ${reason}`,
|
|
412
|
+
recommendation: `Disable the ${method} method in your web server / API framework unless explicitly required.`,
|
|
413
|
+
cvss,
|
|
414
|
+
match: `${method} ${res.statusCode}`,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
catch { /* host down or timeout */ }
|
|
419
|
+
}));
|
|
420
|
+
return findings;
|
|
421
|
+
}
|
|
422
|
+
// ── Run all active checks against a target ────────────────────────────────────
|
|
423
|
+
async runAgainstTarget(target) {
|
|
424
|
+
await this.logger.info(`ActiveScan: full active scan → ${target}`);
|
|
425
|
+
this.audit(`BEGIN active scan: ${target}`);
|
|
426
|
+
const [ports, headers, tls_, cors, methods] = await Promise.all([
|
|
427
|
+
this.scanPorts(target),
|
|
428
|
+
this.auditHeaders(target),
|
|
429
|
+
this.auditTLS(target),
|
|
430
|
+
this.testCORS(target),
|
|
431
|
+
this.testDangerousMethods(target),
|
|
432
|
+
]);
|
|
433
|
+
const all = [...ports, ...headers, ...tls_, ...cors, ...methods];
|
|
434
|
+
const order = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
|
|
435
|
+
all.sort((a, b) => order[a.severity] - order[b.severity]);
|
|
436
|
+
this.audit(`END active scan: ${target} — ${all.length} findings`);
|
|
437
|
+
await this.flushAuditLog();
|
|
438
|
+
return all;
|
|
439
|
+
}
|
|
440
|
+
// ── Audit log ─────────────────────────────────────────────────────────────────
|
|
441
|
+
audit(msg) {
|
|
442
|
+
const ts = new Date().toISOString();
|
|
443
|
+
this.auditLog.push(`[${ts}] ${msg}`);
|
|
444
|
+
}
|
|
445
|
+
async flushAuditLog() {
|
|
446
|
+
try {
|
|
447
|
+
const logPath = path.join(process.cwd(), '.uneven', 'pentest-audit.log');
|
|
448
|
+
await fs.mkdir(path.dirname(logPath), { recursive: true });
|
|
449
|
+
await fs.appendFile(logPath, this.auditLog.join('\n') + '\n', 'utf-8');
|
|
450
|
+
this.auditLog = [];
|
|
451
|
+
}
|
|
452
|
+
catch { /* non-fatal */ }
|
|
453
|
+
}
|
|
454
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Logger } from './logger/index.js';
|
|
2
|
+
export interface AnalystTask {
|
|
3
|
+
id: string;
|
|
4
|
+
tableName: string;
|
|
5
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
6
|
+
resultPath?: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
attempts: number;
|
|
9
|
+
}
|
|
10
|
+
export interface AnalystJob {
|
|
11
|
+
id: string;
|
|
12
|
+
startTime: number;
|
|
13
|
+
goal: string;
|
|
14
|
+
dbUrl: string;
|
|
15
|
+
tasks: AnalystTask[];
|
|
16
|
+
}
|
|
17
|
+
export declare class AnalystJobManager {
|
|
18
|
+
private cacheDir;
|
|
19
|
+
private logger;
|
|
20
|
+
constructor(logger: Logger, baseDir?: string);
|
|
21
|
+
/**
|
|
22
|
+
* Initialize a new job with a set of tables
|
|
23
|
+
*/
|
|
24
|
+
createJob(id: string, dbUrl: string, tables: string[], goal: string): Promise<AnalystJob>;
|
|
25
|
+
/**
|
|
26
|
+
* Save result of a specific task to disk
|
|
27
|
+
*/
|
|
28
|
+
saveTaskResult(jobId: string, tableName: string, result: any): Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Load job state from disk
|
|
31
|
+
*/
|
|
32
|
+
loadJobState(jobId: string): Promise<AnalystJob | null>;
|
|
33
|
+
/**
|
|
34
|
+
* Update job state on disk
|
|
35
|
+
*/
|
|
36
|
+
saveJobState(job: AnalystJob): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* List all pending or failed jobs in cache
|
|
39
|
+
*/
|
|
40
|
+
findIncompleteJobs(): Promise<string[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Clear all analyst cache files
|
|
43
|
+
*/
|
|
44
|
+
clearCache(): Promise<number>;
|
|
45
|
+
private ensureCacheDir;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=analyst-job-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyst-job-manager.d.ts","sourceRoot":"","sources":["../../src/core/analyst-job-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,MAAsB;IAK3D;;OAEG;IACG,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAqB/F;;OAEG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAQpF;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAU7D;;OAEG;IACG,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlD;;OAEG;IACG,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAY7C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;YAerB,cAAc;CAG7B"}
|