@continum/cli 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/README.md +481 -0
- package/SETUP.md +517 -0
- package/dist/api/client.d.ts +17 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +70 -0
- package/dist/api/client.js.map +1 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +104 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +217 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/patterns.d.ts +3 -0
- package/dist/commands/patterns.d.ts.map +1 -0
- package/dist/commands/patterns.js +67 -0
- package/dist/commands/patterns.js.map +1 -0
- package/dist/commands/scan.d.ts +11 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +219 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +61 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/uninstall.d.ts +2 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +87 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/config/default-config.d.ts +3 -0
- package/dist/config/default-config.d.ts.map +1 -0
- package/dist/config/default-config.js +25 -0
- package/dist/config/default-config.js.map +1 -0
- package/dist/config/loader.d.ts +11 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +96 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/git/git-utils.d.ts +8 -0
- package/dist/git/git-utils.d.ts.map +1 -0
- package/dist/git/git-utils.js +130 -0
- package/dist/git/git-utils.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/scanner/local-scan.d.ts +15 -0
- package/dist/scanner/local-scan.d.ts.map +1 -0
- package/dist/scanner/local-scan.js +227 -0
- package/dist/scanner/local-scan.js.map +1 -0
- package/dist/scanner/pattern-updater.d.ts +12 -0
- package/dist/scanner/pattern-updater.d.ts.map +1 -0
- package/dist/scanner/pattern-updater.js +110 -0
- package/dist/scanner/pattern-updater.js.map +1 -0
- package/dist/scanner/patterns.d.ts +5 -0
- package/dist/scanner/patterns.d.ts.map +1 -0
- package/dist/scanner/patterns.js +145 -0
- package/dist/scanner/patterns.js.map +1 -0
- package/dist/types.d.ts +59 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +40 -0
- package/src/api/client.ts +77 -0
- package/src/commands/init.ts +113 -0
- package/src/commands/login.ts +205 -0
- package/src/commands/patterns.ts +68 -0
- package/src/commands/scan.ts +257 -0
- package/src/commands/status.ts +57 -0
- package/src/commands/uninstall.ts +55 -0
- package/src/config/default-config.ts +23 -0
- package/src/config/loader.ts +67 -0
- package/src/git/git-utils.ts +95 -0
- package/src/index.ts +72 -0
- package/src/scanner/local-scan.ts +222 -0
- package/src/scanner/pattern-updater.ts +94 -0
- package/src/scanner/patterns.ts +156 -0
- package/src/types.ts +64 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.initCommand = initCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
9
|
+
const loader_1 = require("../config/loader");
|
|
10
|
+
const git_utils_1 = require("../git/git-utils");
|
|
11
|
+
const client_1 = require("../api/client");
|
|
12
|
+
const default_config_1 = require("../config/default-config");
|
|
13
|
+
async function initCommand(options) {
|
|
14
|
+
if (!options.silent) {
|
|
15
|
+
console.log(chalk_1.default.blue.bold('\n🛡️ Continum CLI Setup\n'));
|
|
16
|
+
}
|
|
17
|
+
// Check if in git repository
|
|
18
|
+
if (!(0, git_utils_1.isGitRepository)()) {
|
|
19
|
+
console.log(chalk_1.default.red('✗ Not a git repository'));
|
|
20
|
+
console.log(chalk_1.default.gray(' Run this command in a git repository'));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
// Check if already initialized
|
|
24
|
+
const existingConfig = (0, loader_1.loadConfig)();
|
|
25
|
+
const hasHook = (0, git_utils_1.hasPreCommitHook)();
|
|
26
|
+
if (existingConfig && hasHook && !options.silent) {
|
|
27
|
+
console.log(chalk_1.default.yellow('⚠️ Continum is already initialized in this repository'));
|
|
28
|
+
const { reinit } = await (0, prompts_1.default)({
|
|
29
|
+
type: 'confirm',
|
|
30
|
+
name: 'reinit',
|
|
31
|
+
message: 'Reinitialize?',
|
|
32
|
+
initial: false
|
|
33
|
+
});
|
|
34
|
+
if (!reinit) {
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Check for credentials - must be logged in first
|
|
39
|
+
const credentials = (0, loader_1.loadCredentials)();
|
|
40
|
+
if (!credentials.apiUrl || !credentials.apiKey) {
|
|
41
|
+
console.log(chalk_1.default.red('✗ Not logged in to Continum\n'));
|
|
42
|
+
console.log(chalk_1.default.gray('Please run `continum login` first to authenticate.\n'));
|
|
43
|
+
console.log(chalk_1.default.blue(' $ continum login\n'));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
// Test connection
|
|
47
|
+
if (!options.silent) {
|
|
48
|
+
console.log(chalk_1.default.gray('\nTesting connection...'));
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const client = new client_1.ContinumApiClient(credentials.apiUrl, credentials.apiKey);
|
|
52
|
+
const status = await client.testConnection();
|
|
53
|
+
if (!options.silent) {
|
|
54
|
+
console.log(chalk_1.default.green(`✓ Connected to Continum (${status.customer})`));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.log(chalk_1.default.red('✗ Failed to connect to Continum API'));
|
|
59
|
+
console.log(chalk_1.default.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
// Get sandbox configuration
|
|
63
|
+
let sandbox = existingConfig?.sandbox || 'default';
|
|
64
|
+
if (!options.silent) {
|
|
65
|
+
const sandboxResponse = await (0, prompts_1.default)({
|
|
66
|
+
type: 'text',
|
|
67
|
+
name: 'sandbox',
|
|
68
|
+
message: 'Sandbox name:',
|
|
69
|
+
initial: sandbox
|
|
70
|
+
});
|
|
71
|
+
if (sandboxResponse.sandbox) {
|
|
72
|
+
sandbox = sandboxResponse.sandbox;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Create config file
|
|
76
|
+
const config = {
|
|
77
|
+
...default_config_1.DEFAULT_CONFIG,
|
|
78
|
+
sandbox,
|
|
79
|
+
apiUrl: credentials.apiUrl,
|
|
80
|
+
apiKey: credentials.apiKey
|
|
81
|
+
};
|
|
82
|
+
(0, loader_1.saveConfig)(config);
|
|
83
|
+
if (!options.silent) {
|
|
84
|
+
console.log(chalk_1.default.green('✓ .continum.json created'));
|
|
85
|
+
}
|
|
86
|
+
// Install git hook
|
|
87
|
+
try {
|
|
88
|
+
(0, git_utils_1.installPreCommitHook)();
|
|
89
|
+
if (!options.silent) {
|
|
90
|
+
console.log(chalk_1.default.green('✓ Pre-commit hook installed'));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.log(chalk_1.default.red('✗ Failed to install pre-commit hook'));
|
|
95
|
+
console.log(chalk_1.default.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
if (!options.silent) {
|
|
99
|
+
console.log(chalk_1.default.blue.bold('\n✓ Continum is ready!\n'));
|
|
100
|
+
console.log(chalk_1.default.gray('Every commit will now be scanned locally.'));
|
|
101
|
+
console.log(chalk_1.default.gray('Violations are also audited by the Continum sandbox.\n'));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":";;;;;AAOA,kCAyGC;AAhHD,kDAA0B;AAC1B,sDAA8B;AAC9B,6CAA4F;AAC5F,gDAA2F;AAC3F,0CAAkD;AAClD,6DAA0D;AAEnD,KAAK,UAAU,WAAW,CAAC,OAA6B;IAC7D,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,6BAA6B;IAC7B,IAAI,CAAC,IAAA,2BAAe,GAAE,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC,CAAC;QAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,+BAA+B;IAC/B,MAAM,cAAc,GAAG,IAAA,mBAAU,GAAE,CAAC;IACpC,MAAM,OAAO,GAAG,IAAA,4BAAgB,GAAE,CAAC;IAEnC,IAAI,cAAc,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,wDAAwD,CAAC,CAAC,CAAC;QACpF,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAA,iBAAO,EAAC;YAC/B,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,eAAe;YACxB,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,kDAAkD;IAClD,MAAM,WAAW,GAAG,IAAA,wBAAe,GAAE,CAAC;IAEtC,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC,CAAC;QAChF,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,kBAAkB;IAClB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,0BAAiB,CAAC,WAAW,CAAC,MAAO,EAAE,WAAW,CAAC,MAAO,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,cAAc,EAAE,CAAC;QAE7C,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,4BAA4B,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;QACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,4BAA4B;IAC5B,IAAI,OAAO,GAAG,cAAc,EAAE,OAAO,IAAI,SAAS,CAAC;IAEnD,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,eAAe,GAAG,MAAM,IAAA,iBAAO,EAAC;YACpC,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,eAAe;YACxB,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC;QAEH,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC;QACpC,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,MAAM,MAAM,GAAG;QACb,GAAG,+BAAc;QACjB,OAAO;QACP,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,MAAM,EAAE,WAAW,CAAC,MAAM;KAC3B,CAAC;IAEF,IAAA,mBAAU,EAAC,MAAM,CAAC,CAAC;IAEnB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,mBAAmB;IACnB,IAAI,CAAC;QACH,IAAA,gCAAoB,GAAE,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;QACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC,CAAC;IACpF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAgBA,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CA+ClD"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.loginCommand = loginCommand;
|
|
40
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
+
const ora_1 = __importDefault(require("ora"));
|
|
42
|
+
const http_1 = require("http");
|
|
43
|
+
const url_1 = require("url");
|
|
44
|
+
const loader_1 = require("../config/loader");
|
|
45
|
+
const client_1 = require("../api/client");
|
|
46
|
+
const CALLBACK_PORT = 8765;
|
|
47
|
+
const CALLBACK_PATH = '/cli/callback';
|
|
48
|
+
async function loginCommand() {
|
|
49
|
+
console.log(chalk_1.default.blue.bold('\n🛡️ Continum CLI Login\n'));
|
|
50
|
+
const apiUrl = process.env.CONTINUM_API_URL || 'https://api.continum.dev';
|
|
51
|
+
const consoleUrl = process.env.CONTINUM_CONSOLE_URL || 'https://console.continum.dev';
|
|
52
|
+
// Generate a random state for security
|
|
53
|
+
const state = generateRandomState();
|
|
54
|
+
// Start local server to receive callback
|
|
55
|
+
const authPromise = startCallbackServer(state);
|
|
56
|
+
// Build auth URL
|
|
57
|
+
const authUrl = `${consoleUrl}/cli/auth?state=${state}&port=${CALLBACK_PORT}`;
|
|
58
|
+
console.log(chalk_1.default.gray('Opening browser for authentication...\n'));
|
|
59
|
+
console.log(chalk_1.default.gray(`If browser doesn't open, visit:\n${authUrl}\n`));
|
|
60
|
+
// Open browser
|
|
61
|
+
const open = await Promise.resolve().then(() => __importStar(require('open')));
|
|
62
|
+
await open.default(authUrl);
|
|
63
|
+
const spinner = (0, ora_1.default)('Waiting for authentication...').start();
|
|
64
|
+
try {
|
|
65
|
+
// Wait for callback
|
|
66
|
+
const authResponse = await authPromise;
|
|
67
|
+
spinner.succeed('Authentication successful!');
|
|
68
|
+
// Save credentials
|
|
69
|
+
(0, loader_1.saveCredentials)(authResponse.apiUrl || apiUrl, authResponse.apiKey);
|
|
70
|
+
// Test connection
|
|
71
|
+
console.log(chalk_1.default.gray('\nTesting connection...'));
|
|
72
|
+
const client = new client_1.ContinumApiClient(authResponse.apiUrl || apiUrl, authResponse.apiKey);
|
|
73
|
+
const status = await client.testConnection();
|
|
74
|
+
console.log(chalk_1.default.green(`✓ Connected to Continum (${status.customer})`));
|
|
75
|
+
console.log(chalk_1.default.blue.bold('\n✓ Login complete!\n'));
|
|
76
|
+
console.log(chalk_1.default.gray('You can now run `continum init` in your project.\n'));
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
spinner.fail('Authentication failed');
|
|
81
|
+
console.log(chalk_1.default.red(`\n✗ ${error instanceof Error ? error.message : 'Unknown error'}\n`));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function generateRandomState() {
|
|
86
|
+
return Math.random().toString(36).substring(2, 15) +
|
|
87
|
+
Math.random().toString(36).substring(2, 15);
|
|
88
|
+
}
|
|
89
|
+
function startCallbackServer(expectedState) {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const timeout = setTimeout(() => {
|
|
92
|
+
server.close();
|
|
93
|
+
reject(new Error('Authentication timeout (5 minutes)'));
|
|
94
|
+
}, 5 * 60 * 1000); // 5 minutes
|
|
95
|
+
const server = (0, http_1.createServer)((req, res) => {
|
|
96
|
+
const parsedUrl = (0, url_1.parse)(req.url || '', true);
|
|
97
|
+
if (parsedUrl.pathname === CALLBACK_PATH) {
|
|
98
|
+
const { state, apiKey, apiUrl, customer, error } = parsedUrl.query;
|
|
99
|
+
// Check for errors
|
|
100
|
+
if (error) {
|
|
101
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
102
|
+
res.end(`
|
|
103
|
+
<!DOCTYPE html>
|
|
104
|
+
<html>
|
|
105
|
+
<head>
|
|
106
|
+
<title>Authentication Failed</title>
|
|
107
|
+
<style>
|
|
108
|
+
body { font-family: system-ui; max-width: 600px; margin: 100px auto; text-align: center; }
|
|
109
|
+
.error { color: #dc2626; font-size: 24px; margin-bottom: 20px; }
|
|
110
|
+
.message { color: #6b7280; }
|
|
111
|
+
</style>
|
|
112
|
+
</head>
|
|
113
|
+
<body>
|
|
114
|
+
<div class="error">❌ Authentication Failed</div>
|
|
115
|
+
<div class="message">${error}</div>
|
|
116
|
+
<p>You can close this window and try again.</p>
|
|
117
|
+
</body>
|
|
118
|
+
</html>
|
|
119
|
+
`);
|
|
120
|
+
clearTimeout(timeout);
|
|
121
|
+
server.close();
|
|
122
|
+
reject(new Error(error));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Verify state
|
|
126
|
+
if (state !== expectedState) {
|
|
127
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
128
|
+
res.end(`
|
|
129
|
+
<!DOCTYPE html>
|
|
130
|
+
<html>
|
|
131
|
+
<head>
|
|
132
|
+
<title>Authentication Failed</title>
|
|
133
|
+
<style>
|
|
134
|
+
body { font-family: system-ui; max-width: 600px; margin: 100px auto; text-align: center; }
|
|
135
|
+
.error { color: #dc2626; font-size: 24px; margin-bottom: 20px; }
|
|
136
|
+
</style>
|
|
137
|
+
</head>
|
|
138
|
+
<body>
|
|
139
|
+
<div class="error">❌ Invalid State</div>
|
|
140
|
+
<p>Security verification failed. Please try again.</p>
|
|
141
|
+
</body>
|
|
142
|
+
</html>
|
|
143
|
+
`);
|
|
144
|
+
clearTimeout(timeout);
|
|
145
|
+
server.close();
|
|
146
|
+
reject(new Error('Invalid state parameter'));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
// Success
|
|
150
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
151
|
+
res.end(`
|
|
152
|
+
<!DOCTYPE html>
|
|
153
|
+
<html>
|
|
154
|
+
<head>
|
|
155
|
+
<title>Authentication Successful</title>
|
|
156
|
+
<style>
|
|
157
|
+
body {
|
|
158
|
+
font-family: system-ui;
|
|
159
|
+
max-width: 600px;
|
|
160
|
+
margin: 100px auto;
|
|
161
|
+
text-align: center;
|
|
162
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
163
|
+
color: white;
|
|
164
|
+
padding: 40px;
|
|
165
|
+
}
|
|
166
|
+
.success { font-size: 48px; margin-bottom: 20px; }
|
|
167
|
+
.title { font-size: 32px; font-weight: bold; margin-bottom: 10px; }
|
|
168
|
+
.message { font-size: 18px; opacity: 0.9; }
|
|
169
|
+
.close {
|
|
170
|
+
margin-top: 30px;
|
|
171
|
+
padding: 12px 24px;
|
|
172
|
+
background: white;
|
|
173
|
+
color: #667eea;
|
|
174
|
+
border: none;
|
|
175
|
+
border-radius: 6px;
|
|
176
|
+
font-size: 16px;
|
|
177
|
+
cursor: pointer;
|
|
178
|
+
font-weight: 600;
|
|
179
|
+
}
|
|
180
|
+
.close:hover { background: #f3f4f6; }
|
|
181
|
+
</style>
|
|
182
|
+
</head>
|
|
183
|
+
<body>
|
|
184
|
+
<div class="success">✓</div>
|
|
185
|
+
<div class="title">Authentication Successful!</div>
|
|
186
|
+
<div class="message">You can now close this window and return to your terminal.</div>
|
|
187
|
+
<button class="close" onclick="window.close()">Close Window</button>
|
|
188
|
+
<script>
|
|
189
|
+
// Auto-close after 3 seconds
|
|
190
|
+
setTimeout(() => window.close(), 3000);
|
|
191
|
+
</script>
|
|
192
|
+
</body>
|
|
193
|
+
</html>
|
|
194
|
+
`);
|
|
195
|
+
clearTimeout(timeout);
|
|
196
|
+
server.close();
|
|
197
|
+
resolve({
|
|
198
|
+
apiKey: apiKey,
|
|
199
|
+
apiUrl: apiUrl,
|
|
200
|
+
customer: customer,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
res.writeHead(404);
|
|
205
|
+
res.end('Not found');
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
server.listen(CALLBACK_PORT, () => {
|
|
209
|
+
// Server started
|
|
210
|
+
});
|
|
211
|
+
server.on('error', (err) => {
|
|
212
|
+
clearTimeout(timeout);
|
|
213
|
+
reject(new Error(`Failed to start callback server: ${err.message}`));
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/commands/patterns.ts"],"names":[],"mappings":"AAKA,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAuB3D;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqCzD"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.patternsUpdateCommand = patternsUpdateCommand;
|
|
7
|
+
exports.patternsListCommand = patternsListCommand;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const loader_1 = require("../config/loader");
|
|
10
|
+
const client_1 = require("../api/client");
|
|
11
|
+
const pattern_updater_1 = require("../scanner/pattern-updater");
|
|
12
|
+
async function patternsUpdateCommand() {
|
|
13
|
+
const credentials = (0, loader_1.loadCredentials)();
|
|
14
|
+
if (!credentials.apiUrl || !credentials.apiKey) {
|
|
15
|
+
console.log(chalk_1.default.red('✗ No API credentials configured'));
|
|
16
|
+
console.log(chalk_1.default.gray(' Run `continum init` to set up credentials'));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
console.log(chalk_1.default.blue('Updating patterns...'));
|
|
20
|
+
try {
|
|
21
|
+
const client = new client_1.ContinumApiClient(credentials.apiUrl, credentials.apiKey);
|
|
22
|
+
const updater = new pattern_updater_1.PatternUpdater(client);
|
|
23
|
+
await updater.updatePatterns(true);
|
|
24
|
+
const patterns = updater.loadPatterns();
|
|
25
|
+
console.log(chalk_1.default.green(`✓ Updated ${patterns.length} patterns`));
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.log(chalk_1.default.red('✗ Failed to update patterns'));
|
|
29
|
+
console.log(chalk_1.default.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function patternsListCommand() {
|
|
34
|
+
const credentials = (0, loader_1.loadCredentials)();
|
|
35
|
+
if (!credentials.apiUrl || !credentials.apiKey) {
|
|
36
|
+
console.log(chalk_1.default.red('✗ No API credentials configured'));
|
|
37
|
+
console.log(chalk_1.default.gray(' Run `continum init` to set up credentials'));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const client = new client_1.ContinumApiClient(credentials.apiUrl, credentials.apiKey);
|
|
42
|
+
const updater = new pattern_updater_1.PatternUpdater(client);
|
|
43
|
+
const patterns = updater.loadPatterns();
|
|
44
|
+
console.log(chalk_1.default.blue.bold('\n🛡️ Pattern Library\n'));
|
|
45
|
+
const byType = {};
|
|
46
|
+
patterns.forEach(p => {
|
|
47
|
+
if (!byType[p.patternType]) {
|
|
48
|
+
byType[p.patternType] = [];
|
|
49
|
+
}
|
|
50
|
+
byType[p.patternType].push(p);
|
|
51
|
+
});
|
|
52
|
+
for (const [type, typePatterns] of Object.entries(byType)) {
|
|
53
|
+
console.log(chalk_1.default.bold(`\n${type} (${typePatterns.length})`));
|
|
54
|
+
typePatterns.forEach(p => {
|
|
55
|
+
console.log(chalk_1.default.gray(` ${p.description}`));
|
|
56
|
+
console.log(chalk_1.default.gray(` Pattern: ${p.pattern}`));
|
|
57
|
+
console.log(chalk_1.default.gray(` Severity: ${p.severity}\n`));
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.log(chalk_1.default.red('✗ Failed to load patterns'));
|
|
63
|
+
console.log(chalk_1.default.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/commands/patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface ScanOptions {
|
|
2
|
+
staged?: boolean;
|
|
3
|
+
hook?: boolean;
|
|
4
|
+
strict?: boolean;
|
|
5
|
+
autoApprove?: boolean;
|
|
6
|
+
warnOnly?: boolean;
|
|
7
|
+
files?: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function scanCommand(options: ScanOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=scan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAWA,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAyGrE"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.scanCommand = scanCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
9
|
+
const local_scan_1 = require("../scanner/local-scan");
|
|
10
|
+
const loader_1 = require("../config/loader");
|
|
11
|
+
const git_utils_1 = require("../git/git-utils");
|
|
12
|
+
const client_1 = require("../api/client");
|
|
13
|
+
const pattern_updater_1 = require("../scanner/pattern-updater");
|
|
14
|
+
async function scanCommand(options) {
|
|
15
|
+
const config = (0, loader_1.loadConfig)();
|
|
16
|
+
const credentials = (0, loader_1.loadCredentials)();
|
|
17
|
+
// Update patterns if we have credentials
|
|
18
|
+
let customPatterns = [];
|
|
19
|
+
if (credentials.apiUrl && credentials.apiKey) {
|
|
20
|
+
try {
|
|
21
|
+
const client = new client_1.ContinumApiClient(credentials.apiUrl, credentials.apiKey);
|
|
22
|
+
const updater = new pattern_updater_1.PatternUpdater(client);
|
|
23
|
+
await updater.updatePatterns();
|
|
24
|
+
customPatterns = updater.loadPatterns();
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
// Continue with built-in patterns only
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Determine which files to scan
|
|
31
|
+
let filesToScan = [];
|
|
32
|
+
if (options.staged) {
|
|
33
|
+
filesToScan = (0, git_utils_1.getStagedFiles)();
|
|
34
|
+
}
|
|
35
|
+
else if (options.files && options.files.length > 0) {
|
|
36
|
+
filesToScan = options.files;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.log(chalk_1.default.red('✗ No files specified'));
|
|
40
|
+
console.log(chalk_1.default.gray(' Use --staged to scan staged files, or provide file paths'));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
if (filesToScan.length === 0) {
|
|
44
|
+
console.log(chalk_1.default.yellow('⚠️ No files to scan'));
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
// Run scan
|
|
48
|
+
if (!options.hook) {
|
|
49
|
+
console.log(chalk_1.default.blue(`\nContinum — scanning ${filesToScan.length} file(s)...\n`));
|
|
50
|
+
}
|
|
51
|
+
const scanner = new local_scan_1.LocalScanner(config, customPatterns);
|
|
52
|
+
const result = await scanner.scanFiles(filesToScan);
|
|
53
|
+
// Handle violations
|
|
54
|
+
if (result.violations.length > 0) {
|
|
55
|
+
console.log(chalk_1.default.red.bold('❌ BLOCKED\n'));
|
|
56
|
+
for (const violation of result.violations) {
|
|
57
|
+
const shouldBlock = config.block.includes(violation.severity);
|
|
58
|
+
const shouldWarn = config.warn.includes(violation.severity);
|
|
59
|
+
if (shouldBlock || shouldWarn) {
|
|
60
|
+
console.log(chalk_1.default.gray(`${violation.file} (line ${violation.line})`));
|
|
61
|
+
console.log(chalk_1.default.gray('─'.repeat(50)));
|
|
62
|
+
console.log(`Type: ${violation.type}`);
|
|
63
|
+
console.log(`Found: ${scanner.redactValue(violation.value)}`);
|
|
64
|
+
console.log(`Severity: ${getSeverityColor(violation.severity)(violation.severity)}`);
|
|
65
|
+
if (violation.message) {
|
|
66
|
+
console.log(`Message: ${violation.message}`);
|
|
67
|
+
}
|
|
68
|
+
console.log();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const blockedCount = result.violations.filter(v => config.block.includes(v.severity)).length;
|
|
72
|
+
if (blockedCount > 0 && !options.warnOnly) {
|
|
73
|
+
console.log(chalk_1.default.red('Fix these before committing.'));
|
|
74
|
+
console.log(chalk_1.default.gray('Override (not recommended): git commit --no-verify\n'));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Handle unknown patterns
|
|
79
|
+
if (result.unknownPatterns.length > 0 && !options.autoApprove) {
|
|
80
|
+
for (const pattern of result.unknownPatterns) {
|
|
81
|
+
await handleUnknownPattern(pattern, scanner, credentials, options);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (result.unknownPatterns.length > 0 && options.autoApprove) {
|
|
85
|
+
// Auto-approve all patterns
|
|
86
|
+
for (const pattern of result.unknownPatterns) {
|
|
87
|
+
await approvePattern(pattern, scanner, credentials, 'HIGH');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Send background audit if we have credentials
|
|
91
|
+
if (credentials.apiUrl && credentials.apiKey && options.staged) {
|
|
92
|
+
try {
|
|
93
|
+
const client = new client_1.ContinumApiClient(credentials.apiUrl, credentials.apiKey);
|
|
94
|
+
const diff = (0, git_utils_1.getStagedDiff)();
|
|
95
|
+
await client.sendSandboxAudit(diff, config.sandbox);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// Silent fail for background audit
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Success
|
|
102
|
+
if (result.clean) {
|
|
103
|
+
if (!options.hook) {
|
|
104
|
+
console.log(chalk_1.default.green('✓ Clean\n'));
|
|
105
|
+
}
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function handleUnknownPattern(pattern, scanner, credentials, options) {
|
|
110
|
+
console.log(chalk_1.default.yellow('\n⚠️ POSSIBLE CREDENTIAL DETECTED\n'));
|
|
111
|
+
console.log(chalk_1.default.gray(`${pattern.context.file} (line ${pattern.context.line})`));
|
|
112
|
+
console.log(chalk_1.default.gray('─'.repeat(50)));
|
|
113
|
+
console.log(`Type: UNKNOWN_PATTERN (${pattern.confidence} confidence)`);
|
|
114
|
+
console.log(`Found: ${scanner.redactValue(pattern.value)}`);
|
|
115
|
+
console.log(`Pattern: ${pattern.suggestedPattern}`);
|
|
116
|
+
console.log('\nThis looks like a credential, but it\'s not in our pattern library.\n');
|
|
117
|
+
if (options.strict) {
|
|
118
|
+
console.log(chalk_1.default.red('Commit blocked (strict mode)'));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
const { action } = await (0, prompts_1.default)({
|
|
122
|
+
type: 'select',
|
|
123
|
+
name: 'action',
|
|
124
|
+
message: 'What would you like to do?',
|
|
125
|
+
choices: [
|
|
126
|
+
{ title: 'Block this commit', value: 'block' },
|
|
127
|
+
{ title: 'Approve pattern and block (will catch in future)', value: 'approve' },
|
|
128
|
+
{ title: 'Ignore this pattern', value: 'ignore' },
|
|
129
|
+
{ title: 'Continue anyway (not recommended)', value: 'continue' }
|
|
130
|
+
]
|
|
131
|
+
});
|
|
132
|
+
switch (action) {
|
|
133
|
+
case 'approve':
|
|
134
|
+
await approvePattern(pattern, scanner, credentials);
|
|
135
|
+
console.log(chalk_1.default.green('✓ Pattern saved to your library'));
|
|
136
|
+
console.log(chalk_1.default.green('✓ This pattern will now be caught locally on future commits\n'));
|
|
137
|
+
process.exit(1);
|
|
138
|
+
case 'block':
|
|
139
|
+
process.exit(1);
|
|
140
|
+
case 'ignore':
|
|
141
|
+
console.log(chalk_1.default.gray('Pattern ignored\n'));
|
|
142
|
+
break;
|
|
143
|
+
case 'continue':
|
|
144
|
+
console.log(chalk_1.default.yellow('⚠️ Continuing without blocking\n'));
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function approvePattern(pattern, scanner, credentials, defaultSeverity) {
|
|
149
|
+
if (!credentials.apiUrl || !credentials.apiKey) {
|
|
150
|
+
console.log(chalk_1.default.red('✗ No API credentials configured'));
|
|
151
|
+
console.log(chalk_1.default.gray(' Run `continum init` to set up credentials'));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
let description = `Custom pattern for ${pattern.context.variableName || 'credential'}`;
|
|
155
|
+
let severity = defaultSeverity || 'HIGH';
|
|
156
|
+
if (!defaultSeverity) {
|
|
157
|
+
const response = await (0, prompts_1.default)([
|
|
158
|
+
{
|
|
159
|
+
type: 'text',
|
|
160
|
+
name: 'description',
|
|
161
|
+
message: 'Pattern description:',
|
|
162
|
+
initial: description
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
type: 'select',
|
|
166
|
+
name: 'severity',
|
|
167
|
+
message: 'Severity level:',
|
|
168
|
+
choices: [
|
|
169
|
+
{ title: 'Critical', value: 'CRITICAL' },
|
|
170
|
+
{ title: 'High', value: 'HIGH' },
|
|
171
|
+
{ title: 'Medium', value: 'MEDIUM' }
|
|
172
|
+
],
|
|
173
|
+
initial: 1
|
|
174
|
+
}
|
|
175
|
+
]);
|
|
176
|
+
if (response.description)
|
|
177
|
+
description = response.description;
|
|
178
|
+
if (response.severity)
|
|
179
|
+
severity = response.severity;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
const client = new client_1.ContinumApiClient(credentials.apiUrl, credentials.apiKey);
|
|
183
|
+
await client.approvePattern({
|
|
184
|
+
pattern: pattern.suggestedPattern,
|
|
185
|
+
patternType: 'CUSTOM',
|
|
186
|
+
description,
|
|
187
|
+
severity,
|
|
188
|
+
exampleValue: scanner.redactValue(pattern.value),
|
|
189
|
+
confidence: pattern.confidence,
|
|
190
|
+
context: {
|
|
191
|
+
file: pattern.context.file,
|
|
192
|
+
line: pattern.context.line,
|
|
193
|
+
variableName: pattern.context.variableName
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
// Update local cache
|
|
197
|
+
const updater = new pattern_updater_1.PatternUpdater(client);
|
|
198
|
+
await updater.updatePatterns(true);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
console.log(chalk_1.default.red('✗ Failed to save pattern'));
|
|
202
|
+
console.log(chalk_1.default.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function getSeverityColor(severity) {
|
|
206
|
+
switch (severity) {
|
|
207
|
+
case 'CRITICAL':
|
|
208
|
+
return chalk_1.default.red.bold;
|
|
209
|
+
case 'HIGH':
|
|
210
|
+
return chalk_1.default.red;
|
|
211
|
+
case 'MEDIUM':
|
|
212
|
+
return chalk_1.default.yellow;
|
|
213
|
+
case 'LOW':
|
|
214
|
+
return chalk_1.default.gray;
|
|
215
|
+
default:
|
|
216
|
+
return chalk_1.default.white;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=scan.js.map
|