@kinosec/cli 0.3.3 → 0.4.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/dist/cli.js +437 -13
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7750,6 +7750,27 @@ function parseApiError(body, status) {
|
|
|
7750
7750
|
requestId: typeof b.request_id === "string" ? b.request_id : "unknown"
|
|
7751
7751
|
});
|
|
7752
7752
|
}
|
|
7753
|
+
var CredentialsResource = class {
|
|
7754
|
+
constructor(api) {
|
|
7755
|
+
this.api = api;
|
|
7756
|
+
}
|
|
7757
|
+
api;
|
|
7758
|
+
async set(assetId, params) {
|
|
7759
|
+
return this.api.put(`v1/assets/${assetId}/credentials`, { json: params }).json();
|
|
7760
|
+
}
|
|
7761
|
+
async get(assetId) {
|
|
7762
|
+
return this.api.get(`v1/assets/${assetId}/credentials`).json();
|
|
7763
|
+
}
|
|
7764
|
+
async remove(assetId) {
|
|
7765
|
+
return this.api.delete(`v1/assets/${assetId}/credentials`).json();
|
|
7766
|
+
}
|
|
7767
|
+
async setTarget(assetId, params) {
|
|
7768
|
+
return this.api.put(`v1/assets/${assetId}/credentials/target`, { json: params }).json();
|
|
7769
|
+
}
|
|
7770
|
+
async removeTarget(assetId) {
|
|
7771
|
+
return this.api.delete(`v1/assets/${assetId}/credentials/target`).json();
|
|
7772
|
+
}
|
|
7773
|
+
};
|
|
7753
7774
|
var IDEMPOTENT_EXACT_PATHS = ["/v1/scans", "/v1/tokens"];
|
|
7754
7775
|
var IDEMPOTENT_SUFFIXES = ["/cancel", "/retest"];
|
|
7755
7776
|
function needsIdempotencyKey(method, path) {
|
|
@@ -7831,8 +7852,10 @@ var KinoSecClient = class {
|
|
|
7831
7852
|
var AssetsResource = class {
|
|
7832
7853
|
constructor(api) {
|
|
7833
7854
|
this.api = api;
|
|
7855
|
+
this.credentials = new CredentialsResource(api);
|
|
7834
7856
|
}
|
|
7835
7857
|
api;
|
|
7858
|
+
credentials;
|
|
7836
7859
|
async list(params = {}) {
|
|
7837
7860
|
return this.api.get("v1/assets", { searchParams: buildSearchParams(params) }).json();
|
|
7838
7861
|
}
|
|
@@ -10912,10 +10935,15 @@ Examples:
|
|
|
10912
10935
|
asset = await client.assets.lookup(domain);
|
|
10913
10936
|
} catch (err) {
|
|
10914
10937
|
if (err instanceof ApiError && err.status === 404) {
|
|
10915
|
-
|
|
10916
|
-
|
|
10938
|
+
asset = await client.assets.create({
|
|
10939
|
+
asset_type: "domain",
|
|
10940
|
+
asset_value: domain,
|
|
10941
|
+
verification_method: "dns"
|
|
10942
|
+
});
|
|
10943
|
+
console.error(`Asset created for ${domain}.`);
|
|
10944
|
+
} else {
|
|
10945
|
+
throw err;
|
|
10917
10946
|
}
|
|
10918
|
-
throw err;
|
|
10919
10947
|
}
|
|
10920
10948
|
targetDisplay = domain;
|
|
10921
10949
|
params = { asset_id: asset.id, scan_mode: options.mode };
|
|
@@ -11099,7 +11127,402 @@ Timeout: pentest still running after ${options.timeout}s.`);
|
|
|
11099
11127
|
}
|
|
11100
11128
|
|
|
11101
11129
|
// src/commands/asset.ts
|
|
11130
|
+
var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
11131
|
+
|
|
11132
|
+
// src/commands/asset-auth.ts
|
|
11102
11133
|
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
11134
|
+
|
|
11135
|
+
// src/lib/resolve-asset.ts
|
|
11136
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;
|
|
11137
|
+
async function resolveAsset(client, identifier) {
|
|
11138
|
+
try {
|
|
11139
|
+
if (UUID_RE.test(identifier)) {
|
|
11140
|
+
return await client.assets.get(identifier);
|
|
11141
|
+
}
|
|
11142
|
+
return await client.assets.lookup(identifier);
|
|
11143
|
+
} catch (error) {
|
|
11144
|
+
if (error instanceof ApiError && error.status === 404) {
|
|
11145
|
+
throw new ApiError({
|
|
11146
|
+
status: 404,
|
|
11147
|
+
code: error.code,
|
|
11148
|
+
detail: `Asset "${identifier}" not found. Create it with:
|
|
11149
|
+
kinosec asset create --type domain --value ${identifier} --verify-method dns
|
|
11150
|
+
or use pentest which auto-creates the asset:
|
|
11151
|
+
kinosec pentest -t ${identifier}`,
|
|
11152
|
+
requestId: error.requestId
|
|
11153
|
+
});
|
|
11154
|
+
}
|
|
11155
|
+
throw error;
|
|
11156
|
+
}
|
|
11157
|
+
}
|
|
11158
|
+
|
|
11159
|
+
// src/lib/prompt.ts
|
|
11160
|
+
import { createInterface as createInterface2 } from "readline";
|
|
11161
|
+
async function promptMasked(label) {
|
|
11162
|
+
if (!isTTY() || !process.stdin.isTTY) {
|
|
11163
|
+
console.error(`Cannot prompt for ${label} in non-interactive mode. Use flags instead.`);
|
|
11164
|
+
process.exit(2);
|
|
11165
|
+
}
|
|
11166
|
+
process.stderr.write(`${label}: `);
|
|
11167
|
+
return new Promise((resolve, reject) => {
|
|
11168
|
+
const chars = [];
|
|
11169
|
+
process.stdin.setRawMode(true);
|
|
11170
|
+
process.stdin.resume();
|
|
11171
|
+
process.stdin.setEncoding("utf-8");
|
|
11172
|
+
const onData = (chunk) => {
|
|
11173
|
+
for (const ch of chunk) {
|
|
11174
|
+
if (ch === "\r" || ch === "\n") {
|
|
11175
|
+
process.stdin.setRawMode(false);
|
|
11176
|
+
process.stdin.pause();
|
|
11177
|
+
process.stdin.removeListener("data", onData);
|
|
11178
|
+
process.stderr.write("\n");
|
|
11179
|
+
resolve(chars.join(""));
|
|
11180
|
+
return;
|
|
11181
|
+
} else if (ch === "") {
|
|
11182
|
+
process.stdin.setRawMode(false);
|
|
11183
|
+
process.stdin.pause();
|
|
11184
|
+
process.stdin.removeListener("data", onData);
|
|
11185
|
+
process.stderr.write("\n");
|
|
11186
|
+
reject(new Error("Cancelled"));
|
|
11187
|
+
return;
|
|
11188
|
+
} else if (ch === "\x7F" || ch === "\b") {
|
|
11189
|
+
if (chars.length > 0) {
|
|
11190
|
+
chars.pop();
|
|
11191
|
+
process.stderr.write("\b \b");
|
|
11192
|
+
}
|
|
11193
|
+
} else if (ch >= " ") {
|
|
11194
|
+
chars.push(ch);
|
|
11195
|
+
process.stderr.write("*");
|
|
11196
|
+
}
|
|
11197
|
+
}
|
|
11198
|
+
};
|
|
11199
|
+
process.stdin.on("data", onData);
|
|
11200
|
+
});
|
|
11201
|
+
}
|
|
11202
|
+
async function promptText(label) {
|
|
11203
|
+
if (!isTTY() || !process.stdin.isTTY) {
|
|
11204
|
+
console.error(`Cannot prompt for ${label} in non-interactive mode. Use flags instead.`);
|
|
11205
|
+
process.exit(2);
|
|
11206
|
+
}
|
|
11207
|
+
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
11208
|
+
const answer = await new Promise((resolve) => {
|
|
11209
|
+
rl.question(`${label}: `, resolve);
|
|
11210
|
+
});
|
|
11211
|
+
rl.close();
|
|
11212
|
+
return answer;
|
|
11213
|
+
}
|
|
11214
|
+
async function promptSelect(message, options) {
|
|
11215
|
+
if (!isTTY() || !process.stdin.isTTY) {
|
|
11216
|
+
console.error("Cannot show interactive menu in non-interactive mode. Use flags instead.");
|
|
11217
|
+
process.exit(2);
|
|
11218
|
+
}
|
|
11219
|
+
console.error(`
|
|
11220
|
+
${message}`);
|
|
11221
|
+
for (let i = 0; i < options.length; i++) {
|
|
11222
|
+
console.error(` ${i + 1}. ${options[i].label} \u2014 ${options[i].description}`);
|
|
11223
|
+
}
|
|
11224
|
+
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
11225
|
+
const answer = await new Promise((resolve) => {
|
|
11226
|
+
rl.question("> ", resolve);
|
|
11227
|
+
});
|
|
11228
|
+
rl.close();
|
|
11229
|
+
const index = parseInt(answer, 10) - 1;
|
|
11230
|
+
if (isNaN(index) || index < 0 || index >= options.length) {
|
|
11231
|
+
console.error("Invalid selection.");
|
|
11232
|
+
process.exit(2);
|
|
11233
|
+
}
|
|
11234
|
+
return options[index].value;
|
|
11235
|
+
}
|
|
11236
|
+
|
|
11237
|
+
// src/commands/asset-auth.ts
|
|
11238
|
+
var VALID_AUTH_TYPES = ["bearer", "basic", "apikey", "session", "cookie"];
|
|
11239
|
+
var AUTH_TYPE_OPTIONS = [
|
|
11240
|
+
{ value: "bearer", label: "Bearer Token", description: "Authorization header with token" },
|
|
11241
|
+
{ value: "apikey", label: "API Key", description: "Custom header (e.g. X-API-Key)" },
|
|
11242
|
+
{ value: "basic", label: "Basic Auth", description: "Username + password" },
|
|
11243
|
+
{ value: "session", label: "Session Login", description: "Replays login form POST" },
|
|
11244
|
+
{ value: "cookie", label: "Cookie / Session", description: "Inject browser cookies" }
|
|
11245
|
+
];
|
|
11246
|
+
async function collectCredentialsInteractive(authType) {
|
|
11247
|
+
const params = { auth_type: authType, credentials: {} };
|
|
11248
|
+
switch (authType) {
|
|
11249
|
+
case "bearer": {
|
|
11250
|
+
params.credentials = { token: await promptMasked("Bearer token") };
|
|
11251
|
+
break;
|
|
11252
|
+
}
|
|
11253
|
+
case "basic": {
|
|
11254
|
+
const username = await promptText("Username");
|
|
11255
|
+
const password = await promptMasked("Password");
|
|
11256
|
+
params.credentials = { username, password };
|
|
11257
|
+
break;
|
|
11258
|
+
}
|
|
11259
|
+
case "apikey": {
|
|
11260
|
+
const key = await promptMasked("API key");
|
|
11261
|
+
const headerName = await promptText("Header name (e.g. X-API-Key)");
|
|
11262
|
+
params.credentials = { key };
|
|
11263
|
+
params.auth_header_name = headerName;
|
|
11264
|
+
break;
|
|
11265
|
+
}
|
|
11266
|
+
case "session": {
|
|
11267
|
+
const username = await promptText("Username or email");
|
|
11268
|
+
const password = await promptMasked("Password");
|
|
11269
|
+
const endpoint = await promptText("Login endpoint URL");
|
|
11270
|
+
params.credentials = { email: username, password };
|
|
11271
|
+
params.auth_endpoint_url = endpoint;
|
|
11272
|
+
break;
|
|
11273
|
+
}
|
|
11274
|
+
case "cookie": {
|
|
11275
|
+
params.credentials = { cookie: await promptMasked("Cookie string") };
|
|
11276
|
+
break;
|
|
11277
|
+
}
|
|
11278
|
+
}
|
|
11279
|
+
return params;
|
|
11280
|
+
}
|
|
11281
|
+
function collectCredentialsFromFlags(options) {
|
|
11282
|
+
const authType = options.type;
|
|
11283
|
+
const params = { auth_type: authType, credentials: {} };
|
|
11284
|
+
switch (authType) {
|
|
11285
|
+
case "bearer": {
|
|
11286
|
+
if (!options.bearerToken) {
|
|
11287
|
+
console.error("--bearer-token is required for bearer auth type.");
|
|
11288
|
+
process.exit(2);
|
|
11289
|
+
}
|
|
11290
|
+
params.credentials = { token: options.bearerToken };
|
|
11291
|
+
break;
|
|
11292
|
+
}
|
|
11293
|
+
case "basic": {
|
|
11294
|
+
if (!options.username || !options.password) {
|
|
11295
|
+
console.error("--username and --password are required for basic auth type.");
|
|
11296
|
+
process.exit(2);
|
|
11297
|
+
}
|
|
11298
|
+
params.credentials = { username: options.username, password: options.password };
|
|
11299
|
+
break;
|
|
11300
|
+
}
|
|
11301
|
+
case "apikey": {
|
|
11302
|
+
if (!options.key || !options.headerName) {
|
|
11303
|
+
console.error("--key and --header-name are required for apikey auth type.");
|
|
11304
|
+
process.exit(2);
|
|
11305
|
+
}
|
|
11306
|
+
params.credentials = { key: options.key };
|
|
11307
|
+
params.auth_header_name = options.headerName;
|
|
11308
|
+
break;
|
|
11309
|
+
}
|
|
11310
|
+
case "session": {
|
|
11311
|
+
if (!options.username || !options.password || !options.authEndpoint) {
|
|
11312
|
+
console.error("--username, --password, and --auth-endpoint are required for session auth type.");
|
|
11313
|
+
process.exit(2);
|
|
11314
|
+
}
|
|
11315
|
+
params.credentials = { email: options.username, password: options.password };
|
|
11316
|
+
params.auth_endpoint_url = options.authEndpoint;
|
|
11317
|
+
break;
|
|
11318
|
+
}
|
|
11319
|
+
case "cookie": {
|
|
11320
|
+
if (!options.cookie) {
|
|
11321
|
+
console.error("--cookie is required for cookie auth type.");
|
|
11322
|
+
process.exit(2);
|
|
11323
|
+
}
|
|
11324
|
+
params.credentials = { cookie: options.cookie };
|
|
11325
|
+
break;
|
|
11326
|
+
}
|
|
11327
|
+
}
|
|
11328
|
+
if (options.context) {
|
|
11329
|
+
params.context_text = options.context;
|
|
11330
|
+
}
|
|
11331
|
+
return params;
|
|
11332
|
+
}
|
|
11333
|
+
function registerAssetAuthCommand(assetCmd) {
|
|
11334
|
+
const auth = assetCmd.command("auth").description("Manage asset authentication credentials");
|
|
11335
|
+
auth.command("set").description("Configure authentication credentials for an asset").argument("<asset>", "Asset domain name or UUID").option("--type <type>", "Auth type: bearer, basic, apikey, session, cookie").option("--bearer-token <value>", "Bearer token (bearer type)").option("--username <value>", "Username or email (basic, session types)").option("--password <value>", "Password (basic, session types)").option("--key <value>", "API key value (apikey type)").option("--header-name <name>", "Custom header name (apikey type, e.g. X-API-Key)").option("--cookie <value>", "Cookie string (cookie type)").option("--auth-endpoint <url>", "Login endpoint URL (session type)").option("--context <text>", "Optional notes about the auth configuration").addHelpText("after", `
|
|
11336
|
+
Examples:
|
|
11337
|
+
$ kinosec asset auth set example.com # interactive
|
|
11338
|
+
$ kinosec asset auth set example.com --type bearer --bearer-token "$TOKEN"
|
|
11339
|
+
$ kinosec asset auth set example.com --type basic --username admin --password "$PASS"
|
|
11340
|
+
$ kinosec asset auth set example.com --type apikey --key "$KEY" --header-name "X-API-Key"
|
|
11341
|
+
$ kinosec asset auth set example.com --type session --username admin@example.com --password "$PASS" --auth-endpoint "https://example.com/api/login"
|
|
11342
|
+
$ kinosec asset auth set example.com --type cookie --cookie "session_id=abc123"
|
|
11343
|
+
|
|
11344
|
+
Security: Pass credentials via environment variables to avoid shell history exposure.`).action(async (assetIdentifier, options) => {
|
|
11345
|
+
try {
|
|
11346
|
+
const opts = auth.parent.parent.opts();
|
|
11347
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
11348
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
11349
|
+
const asset = await resolveAsset(client, assetIdentifier);
|
|
11350
|
+
let params;
|
|
11351
|
+
if (options.type) {
|
|
11352
|
+
if (!VALID_AUTH_TYPES.includes(options.type)) {
|
|
11353
|
+
console.error(`Invalid auth type "${options.type}". Must be: ${VALID_AUTH_TYPES.join(", ")}`);
|
|
11354
|
+
process.exit(2);
|
|
11355
|
+
}
|
|
11356
|
+
params = collectCredentialsFromFlags(options);
|
|
11357
|
+
} else {
|
|
11358
|
+
const authType = await promptSelect("Authentication type:", AUTH_TYPE_OPTIONS);
|
|
11359
|
+
params = await collectCredentialsInteractive(authType);
|
|
11360
|
+
if (options.context) {
|
|
11361
|
+
params.context_text = options.context;
|
|
11362
|
+
}
|
|
11363
|
+
}
|
|
11364
|
+
const result = await client.assets.credentials.set(asset.id, params);
|
|
11365
|
+
if (opts.json || opts.output === "json") {
|
|
11366
|
+
printJson({ ...result, asset: asset.asset_value });
|
|
11367
|
+
return;
|
|
11368
|
+
}
|
|
11369
|
+
printSuccess(`Credentials saved for ${asset.asset_value} (${result.auth_type})`);
|
|
11370
|
+
} catch (error) {
|
|
11371
|
+
handleError(error);
|
|
11372
|
+
}
|
|
11373
|
+
});
|
|
11374
|
+
auth.command("show").description("Show authentication configuration for an asset").argument("<asset>", "Asset domain name or UUID").addHelpText("after", `
|
|
11375
|
+
Examples:
|
|
11376
|
+
$ kinosec asset auth show example.com
|
|
11377
|
+
$ kinosec asset auth show example.com --json`).action(async (assetIdentifier) => {
|
|
11378
|
+
try {
|
|
11379
|
+
const opts = auth.parent.parent.opts();
|
|
11380
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
11381
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
11382
|
+
const asset = await resolveAsset(client, assetIdentifier);
|
|
11383
|
+
const creds = await client.assets.credentials.get(asset.id);
|
|
11384
|
+
if (opts.json || opts.output === "json") {
|
|
11385
|
+
printJson({ asset: asset.asset_value, ...creds });
|
|
11386
|
+
return;
|
|
11387
|
+
}
|
|
11388
|
+
if (!creds.has_credentials) {
|
|
11389
|
+
console.log(`Asset: ${asset.asset_value}`);
|
|
11390
|
+
console.log(`Auth Type: ${import_picocolors6.default.dim("(none)")}`);
|
|
11391
|
+
console.log(`Has Credentials: no`);
|
|
11392
|
+
return;
|
|
11393
|
+
}
|
|
11394
|
+
console.log(`Asset: ${asset.asset_value}`);
|
|
11395
|
+
console.log(`Auth Type: ${creds.auth_type}`);
|
|
11396
|
+
console.log(`Has Credentials: yes`);
|
|
11397
|
+
if (creds.auth_header_name) {
|
|
11398
|
+
console.log(`Header Name: ${creds.auth_header_name}`);
|
|
11399
|
+
}
|
|
11400
|
+
if (creds.auth_endpoint_url) {
|
|
11401
|
+
console.log(`Auth Endpoint: ${creds.auth_endpoint_url}`);
|
|
11402
|
+
}
|
|
11403
|
+
if (creds.context_text) {
|
|
11404
|
+
console.log(`Notes: ${creds.context_text}`);
|
|
11405
|
+
}
|
|
11406
|
+
if (creds.target_account) {
|
|
11407
|
+
const ta = creds.target_account;
|
|
11408
|
+
const hasAny = ta.has_username || ta.has_cookie || ta.has_bearer_token || ta.has_apikey;
|
|
11409
|
+
if (hasAny) {
|
|
11410
|
+
const detail = ta.has_username ? "username" : ta.has_bearer_token ? "bearer token" : ta.has_cookie ? "cookie" : "API key";
|
|
11411
|
+
console.log(`Target Account: ${import_picocolors6.default.green("configured")} (${detail})`);
|
|
11412
|
+
} else {
|
|
11413
|
+
console.log(`Target Account: ${import_picocolors6.default.dim("not configured")}`);
|
|
11414
|
+
}
|
|
11415
|
+
}
|
|
11416
|
+
} catch (error) {
|
|
11417
|
+
handleError(error);
|
|
11418
|
+
}
|
|
11419
|
+
});
|
|
11420
|
+
auth.command("remove").description("Remove all credentials for an asset").argument("<asset>", "Asset domain name or UUID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
11421
|
+
Examples:
|
|
11422
|
+
$ kinosec asset auth remove example.com
|
|
11423
|
+
$ kinosec asset auth remove example.com --yes`).action(async (assetIdentifier, options) => {
|
|
11424
|
+
try {
|
|
11425
|
+
const opts = auth.parent.parent.opts();
|
|
11426
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
11427
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
11428
|
+
const asset = await resolveAsset(client, assetIdentifier);
|
|
11429
|
+
await confirmDestructive(`Remove all credentials for ${asset.asset_value}?`, Boolean(options.yes));
|
|
11430
|
+
await client.assets.credentials.remove(asset.id);
|
|
11431
|
+
if (opts.json || opts.output === "json") {
|
|
11432
|
+
printJson({ deleted: true, asset: asset.asset_value });
|
|
11433
|
+
return;
|
|
11434
|
+
}
|
|
11435
|
+
printSuccess(`Credentials removed for ${asset.asset_value}`);
|
|
11436
|
+
} catch (error) {
|
|
11437
|
+
handleError(error);
|
|
11438
|
+
}
|
|
11439
|
+
});
|
|
11440
|
+
auth.command("set-target").description("Configure IDOR target account credentials").argument("<asset>", "Asset domain name or UUID").option("--target-username <value>", "Target account username or email").option("--target-password <value>", "Target account password").option("--target-totp-secret <value>", "Target account TOTP secret").option("--target-token <value>", "Target bearer token").option("--target-cookie <value>", "Target cookie string").option("--target-key <value>", "Target API key").addHelpText("after", `
|
|
11441
|
+
Examples:
|
|
11442
|
+
$ kinosec asset auth set-target example.com # interactive
|
|
11443
|
+
$ kinosec asset auth set-target example.com --target-username victim@example.com --target-password "$PASS"
|
|
11444
|
+
$ kinosec asset auth set-target example.com --target-token "$VICTIM_TOKEN"
|
|
11445
|
+
$ kinosec asset auth set-target example.com --target-cookie "session_id=victim_abc"`).action(async (assetIdentifier, options) => {
|
|
11446
|
+
try {
|
|
11447
|
+
const opts = auth.parent.parent.opts();
|
|
11448
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
11449
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
11450
|
+
const asset = await resolveAsset(client, assetIdentifier);
|
|
11451
|
+
const creds = await client.assets.credentials.get(asset.id);
|
|
11452
|
+
if (!creds.has_credentials || !creds.auth_type) {
|
|
11453
|
+
console.error(`No primary credentials configured for ${asset.asset_value}. Run 'kinosec asset auth set' first.`);
|
|
11454
|
+
process.exit(2);
|
|
11455
|
+
}
|
|
11456
|
+
const isTokenBased = creds.auth_type === "bearer" || creds.auth_type === "apikey" || creds.auth_type === "cookie";
|
|
11457
|
+
const hasAnyFlag = options.targetUsername || options.targetPassword || options.targetTotpSecret || options.targetToken || options.targetCookie || options.targetKey;
|
|
11458
|
+
let targetParams;
|
|
11459
|
+
if (hasAnyFlag) {
|
|
11460
|
+
targetParams = {
|
|
11461
|
+
username: options.targetUsername,
|
|
11462
|
+
password: options.targetPassword,
|
|
11463
|
+
totp_secret: options.targetTotpSecret,
|
|
11464
|
+
bearer_token: options.targetToken,
|
|
11465
|
+
cookie: options.targetCookie,
|
|
11466
|
+
apikey: options.targetKey
|
|
11467
|
+
};
|
|
11468
|
+
} else {
|
|
11469
|
+
console.error(`
|
|
11470
|
+
Primary auth type: ${creds.auth_type}
|
|
11471
|
+
`);
|
|
11472
|
+
if (isTokenBased) {
|
|
11473
|
+
if (creds.auth_type === "bearer") {
|
|
11474
|
+
const targetToken = await promptMasked("Target bearer token");
|
|
11475
|
+
targetParams = { bearer_token: targetToken };
|
|
11476
|
+
} else if (creds.auth_type === "cookie") {
|
|
11477
|
+
const targetCookie = await promptMasked("Target cookie string");
|
|
11478
|
+
targetParams = { cookie: targetCookie };
|
|
11479
|
+
} else {
|
|
11480
|
+
const targetKey = await promptMasked("Target API key");
|
|
11481
|
+
targetParams = { apikey: targetKey };
|
|
11482
|
+
}
|
|
11483
|
+
} else {
|
|
11484
|
+
const username = await promptText("Target account username");
|
|
11485
|
+
const password = await promptMasked("Target account password");
|
|
11486
|
+
targetParams = { username, password };
|
|
11487
|
+
const totp = await promptText("Target TOTP secret (optional, Enter to skip)");
|
|
11488
|
+
if (totp) {
|
|
11489
|
+
targetParams.totp_secret = totp;
|
|
11490
|
+
}
|
|
11491
|
+
}
|
|
11492
|
+
}
|
|
11493
|
+
await client.assets.credentials.setTarget(asset.id, targetParams);
|
|
11494
|
+
if (opts.json || opts.output === "json") {
|
|
11495
|
+
printJson({ saved: true, asset: asset.asset_value });
|
|
11496
|
+
return;
|
|
11497
|
+
}
|
|
11498
|
+
printSuccess(`Target account saved for ${asset.asset_value} (IDOR testing)`);
|
|
11499
|
+
} catch (error) {
|
|
11500
|
+
handleError(error);
|
|
11501
|
+
}
|
|
11502
|
+
});
|
|
11503
|
+
auth.command("remove-target").description("Remove target account credentials (keeps primary credentials)").argument("<asset>", "Asset domain name or UUID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
11504
|
+
Examples:
|
|
11505
|
+
$ kinosec asset auth remove-target example.com
|
|
11506
|
+
$ kinosec asset auth remove-target example.com --yes`).action(async (assetIdentifier, options) => {
|
|
11507
|
+
try {
|
|
11508
|
+
const opts = auth.parent.parent.opts();
|
|
11509
|
+
const { token, apiUrl } = resolveToken(opts);
|
|
11510
|
+
const client = new KinoSecClient({ apiUrl, token });
|
|
11511
|
+
const asset = await resolveAsset(client, assetIdentifier);
|
|
11512
|
+
await confirmDestructive(`Remove target account for ${asset.asset_value}?`, Boolean(options.yes));
|
|
11513
|
+
await client.assets.credentials.removeTarget(asset.id);
|
|
11514
|
+
if (opts.json || opts.output === "json") {
|
|
11515
|
+
printJson({ deleted: true, asset: asset.asset_value });
|
|
11516
|
+
return;
|
|
11517
|
+
}
|
|
11518
|
+
printSuccess(`Target account removed for ${asset.asset_value}`);
|
|
11519
|
+
} catch (error) {
|
|
11520
|
+
handleError(error);
|
|
11521
|
+
}
|
|
11522
|
+
});
|
|
11523
|
+
}
|
|
11524
|
+
|
|
11525
|
+
// src/commands/asset.ts
|
|
11103
11526
|
function registerAssetCommand(program3) {
|
|
11104
11527
|
const asset = program3.command("asset").description("Manage assets");
|
|
11105
11528
|
asset.command("list").description("List assets").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").option("--search <term>", "Filter by asset value (substring match)").option("--status <status>", "Filter by status (verified, unverified)").option("--type <type>", "Filter by type (domain, subdomain, ip)").addHelpText("after", `
|
|
@@ -11138,7 +11561,7 @@ Examples:
|
|
|
11138
11561
|
])
|
|
11139
11562
|
);
|
|
11140
11563
|
if (result.next_cursor) {
|
|
11141
|
-
console.log(
|
|
11564
|
+
console.log(import_picocolors7.default.dim(`
|
|
11142
11565
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
11143
11566
|
}
|
|
11144
11567
|
} catch (error) {
|
|
@@ -11246,10 +11669,11 @@ Examples:
|
|
|
11246
11669
|
handleError(error);
|
|
11247
11670
|
}
|
|
11248
11671
|
});
|
|
11672
|
+
registerAssetAuthCommand(asset);
|
|
11249
11673
|
}
|
|
11250
11674
|
|
|
11251
11675
|
// src/commands/finding.ts
|
|
11252
|
-
var
|
|
11676
|
+
var import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
11253
11677
|
function formatPoc(poc) {
|
|
11254
11678
|
if (poc == null) return null;
|
|
11255
11679
|
if (typeof poc === "string") return poc.trim() || null;
|
|
@@ -11290,7 +11714,7 @@ Examples:
|
|
|
11290
11714
|
])
|
|
11291
11715
|
);
|
|
11292
11716
|
if (result.next_cursor) {
|
|
11293
|
-
console.log(
|
|
11717
|
+
console.log(import_picocolors8.default.dim(`
|
|
11294
11718
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
11295
11719
|
}
|
|
11296
11720
|
} catch (error) {
|
|
@@ -11378,7 +11802,7 @@ Examples:
|
|
|
11378
11802
|
}
|
|
11379
11803
|
|
|
11380
11804
|
// src/commands/report.ts
|
|
11381
|
-
var
|
|
11805
|
+
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
11382
11806
|
function renderMarkdown(data) {
|
|
11383
11807
|
const { scan, findings } = data;
|
|
11384
11808
|
const lines = [];
|
|
@@ -11455,7 +11879,7 @@ Examples:
|
|
|
11455
11879
|
])
|
|
11456
11880
|
);
|
|
11457
11881
|
if (result.next_cursor) {
|
|
11458
|
-
console.log(
|
|
11882
|
+
console.log(import_picocolors9.default.dim(`
|
|
11459
11883
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
11460
11884
|
}
|
|
11461
11885
|
} catch (error) {
|
|
@@ -11526,7 +11950,7 @@ Examples:
|
|
|
11526
11950
|
}
|
|
11527
11951
|
|
|
11528
11952
|
// src/commands/token.ts
|
|
11529
|
-
var
|
|
11953
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
11530
11954
|
function registerTokenCommand(program3) {
|
|
11531
11955
|
const token = program3.command("token").description("Manage API tokens");
|
|
11532
11956
|
token.command("list").description("List API tokens").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
|
|
@@ -11561,7 +11985,7 @@ Examples:
|
|
|
11561
11985
|
])
|
|
11562
11986
|
);
|
|
11563
11987
|
if (result.next_cursor) {
|
|
11564
|
-
console.log(
|
|
11988
|
+
console.log(import_picocolors10.default.dim(`
|
|
11565
11989
|
More results available. Use --cursor ${result.next_cursor}`));
|
|
11566
11990
|
}
|
|
11567
11991
|
} catch (error) {
|
|
@@ -11588,7 +12012,7 @@ Examples:
|
|
|
11588
12012
|
}
|
|
11589
12013
|
printSuccess(`Token created: ${result.name}`);
|
|
11590
12014
|
console.log("");
|
|
11591
|
-
console.log(
|
|
12015
|
+
console.log(import_picocolors10.default.bold(" Token: ") + result.token);
|
|
11592
12016
|
console.log("");
|
|
11593
12017
|
printWarning("Save this token \u2014 it will not be shown again.");
|
|
11594
12018
|
console.log(` ID: ${result.id}`);
|
|
@@ -11621,7 +12045,7 @@ Examples:
|
|
|
11621
12045
|
|
|
11622
12046
|
// src/cli.ts
|
|
11623
12047
|
var program2 = new Command();
|
|
11624
|
-
program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.
|
|
12048
|
+
program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.4.0", "-v, --version").option("--token <pat>", "API token (overrides KINOSEC_TOKEN and config)").option("--api-url <url>", "API base URL (overrides config)").option("--output <format>", "Output format: text or json", "text").option("--json", "Shorthand for --output json").option("--no-color", "Disable colors");
|
|
11625
12049
|
registerAuthCommand(program2);
|
|
11626
12050
|
registerConfigCommand(program2);
|
|
11627
12051
|
registerMeCommand(program2);
|
|
@@ -11630,7 +12054,7 @@ registerAssetCommand(program2);
|
|
|
11630
12054
|
registerFindingCommand(program2);
|
|
11631
12055
|
registerReportCommand(program2);
|
|
11632
12056
|
registerTokenCommand(program2);
|
|
11633
|
-
program2.parseAsync(process.argv).then(() => checkForUpdate("0.
|
|
12057
|
+
program2.parseAsync(process.argv).then(() => checkForUpdate("0.4.0")).catch(handleError);
|
|
11634
12058
|
/*! Bundled license information:
|
|
11635
12059
|
|
|
11636
12060
|
ky/distribution/index.js:
|