@brunsforge/aarm 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.
Files changed (43) hide show
  1. package/README.md +1180 -0
  2. package/dist/aarm.cjs +39260 -0
  3. package/dist/aarm.mjs +39262 -0
  4. package/dist/commands/apps.d.ts +2 -0
  5. package/dist/commands/apps.js +32 -0
  6. package/dist/commands/apps.js.map +1 -0
  7. package/dist/commands/preflight.d.ts +2 -0
  8. package/dist/commands/preflight.js +160 -0
  9. package/dist/commands/preflight.js.map +1 -0
  10. package/dist/commands/report.d.ts +2 -0
  11. package/dist/commands/report.js +166 -0
  12. package/dist/commands/report.js.map +1 -0
  13. package/dist/commands/secrets.d.ts +2 -0
  14. package/dist/commands/secrets.js +88 -0
  15. package/dist/commands/secrets.js.map +1 -0
  16. package/dist/commands/tenants.d.ts +2 -0
  17. package/dist/commands/tenants.js +209 -0
  18. package/dist/commands/tenants.js.map +1 -0
  19. package/dist/commands/usage.d.ts +2 -0
  20. package/dist/commands/usage.js +203 -0
  21. package/dist/commands/usage.js.map +1 -0
  22. package/dist/config/ConfigStore.d.ts +12 -0
  23. package/dist/config/ConfigStore.js +53 -0
  24. package/dist/config/ConfigStore.js.map +1 -0
  25. package/dist/config/CredentialStore.d.ts +8 -0
  26. package/dist/config/CredentialStore.js +29 -0
  27. package/dist/config/CredentialStore.js.map +1 -0
  28. package/dist/config/HistoryStore.d.ts +19 -0
  29. package/dist/config/HistoryStore.js +64 -0
  30. package/dist/config/HistoryStore.js.map +1 -0
  31. package/dist/exitCodes.d.ts +9 -0
  32. package/dist/exitCodes.js +10 -0
  33. package/dist/exitCodes.js.map +1 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.js +38 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/output/formatters.d.ts +10 -0
  38. package/dist/output/formatters.js +129 -0
  39. package/dist/output/formatters.js.map +1 -0
  40. package/dist/shared/context.d.ts +31 -0
  41. package/dist/shared/context.js +124 -0
  42. package/dist/shared/context.js.map +1 -0
  43. package/package.json +43 -0
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerAppsCommand(program: Command): void;
@@ -0,0 +1,32 @@
1
+ import { createResultEnvelope, envelopeToJson, } from '@brunsforge/azure-app-registration-monitor';
2
+ import { buildContext, handleError } from '../shared/context.js';
3
+ import { printAppsTable } from '../output/formatters.js';
4
+ export function registerAppsCommand(program) {
5
+ const apps = program
6
+ .command('apps')
7
+ .description('Query App Registrations')
8
+ .action(() => apps.help()); // show help when no subcommand given
9
+ apps
10
+ .command('list')
11
+ .description('List all App Registrations and their secret risk summary')
12
+ .option('--include-owners', 'Resolve application owners (requires Directory.Read.All)')
13
+ .action(async (cmdOpts) => {
14
+ try {
15
+ const ctx = await buildContext(program.opts());
16
+ const inventory = await ctx.inventoryService.getInventory({
17
+ includeOwners: cmdOpts.includeOwners,
18
+ });
19
+ if (ctx.isJson) {
20
+ const envelope = createResultEnvelope(inventory, ctx.tenantId, ctx.environmentName);
21
+ process.stdout.write(envelopeToJson(envelope) + '\n');
22
+ }
23
+ else {
24
+ printAppsTable(inventory);
25
+ }
26
+ }
27
+ catch (err) {
28
+ handleError(err);
29
+ }
30
+ });
31
+ }
32
+ //# sourceMappingURL=apps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../src/commands/apps.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,EACpB,cAAc,GACf,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAa,MAAM,yBAAyB,CAAC;AAEpE,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,MAAM,IAAI,GAAG,OAAO;SACjB,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,yBAAyB,CAAC;SACtC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,qCAAqC;IAEnE,IAAI;SACD,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,0DAA0D,CAAC;SACvE,MAAM,CAAC,kBAAkB,EAAE,0DAA0D,CAAC;SACtF,MAAM,CAAC,KAAK,EAAE,OAAoC,EAAE,EAAE;QACrD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAC/C,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,YAAY,CAAC;gBACxD,aAAa,EAAE,OAAO,CAAC,aAAa;aACrC,CAAC,CAAC;YAEH,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;gBACf,MAAM,QAAQ,GAAG,oBAAoB,CACnC,SAAS,EACT,GAAG,CAAC,QAAQ,EACZ,GAAG,CAAC,eAAe,CACpB,CAAC;gBACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;YACxD,CAAC;iBAAM,CAAC;gBACN,cAAc,CAAC,SAAS,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerPreflightCommand(program: Command): void;
@@ -0,0 +1,160 @@
1
+ import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
+ import { createResultEnvelope, envelopeToJson, isDelegatedMode, PERMISSION_DETAILS, } from '@brunsforge/azure-app-registration-monitor';
4
+ import { buildContext, handleError } from '../shared/context.js';
5
+ import { ConfigStore } from '../config/ConfigStore.js';
6
+ import { HistoryStore } from '../config/HistoryStore.js';
7
+ function tick(value) {
8
+ return value ? chalk.green('[✓]') : chalk.red('[ ]');
9
+ }
10
+ function printPreflightTable(result) {
11
+ process.stdout.write(`\nPreflight — ${chalk.bold(result.tenantId)}\n`);
12
+ process.stdout.write(`Checked at : ${result.checkedAt}\n\n`);
13
+ const statusTable = new Table({ style: { compact: true } });
14
+ statusTable.push(['Authentication', result.authValid ? chalk.green('OK') : chalk.red('FAILED')], ['Graph reachable', result.graphReachable ? chalk.green('OK') : chalk.red('FAILED')]);
15
+ process.stdout.write(statusTable.toString() + '\n\n');
16
+ process.stdout.write(chalk.bold('Capabilities\n'));
17
+ const capTable = new Table({
18
+ head: ['Capability', 'Status'],
19
+ style: { head: ['cyan'] },
20
+ });
21
+ for (const [key, value] of Object.entries(result.capabilities)) {
22
+ capTable.push([
23
+ key,
24
+ `${tick(value)} ${value ? 'Available' : chalk.dim('Unavailable')}`,
25
+ ]);
26
+ }
27
+ process.stdout.write(capTable.toString() + '\n');
28
+ if (result.missingPermissions.length > 0) {
29
+ process.stdout.write(chalk.bold('\nMissing permissions\n'));
30
+ for (const p of result.missingPermissions) {
31
+ process.stdout.write(` ${chalk.red('✗')} ${p}\n`);
32
+ }
33
+ }
34
+ if (result.warnings.length > 0) {
35
+ process.stdout.write(chalk.bold('\nWarnings\n'));
36
+ for (const w of result.warnings) {
37
+ process.stdout.write(` ${chalk.yellow('!')} ${w}\n`);
38
+ }
39
+ }
40
+ if (result.errors.length > 0) {
41
+ process.stdout.write(chalk.bold('\nErrors\n'));
42
+ for (const e of result.errors) {
43
+ process.stdout.write(` ${chalk.red('✗')} ${e}\n`);
44
+ }
45
+ }
46
+ }
47
+ export function registerPreflightCommand(program) {
48
+ const preflight = program
49
+ .command('preflight')
50
+ .description('Run and display tenant capability preflight checks')
51
+ .action(() => preflight.help());
52
+ preflight
53
+ .command('run')
54
+ .description('Run a full preflight capability check against the tenant')
55
+ .action(async () => {
56
+ try {
57
+ const ctx = await buildContext(program.opts());
58
+ // Progress goes to stderr so stdout stays clean JSON when --output json
59
+ if (!ctx.isJson) {
60
+ process.stderr.write(chalk.dim(`Running preflight for ${ctx.tenantId}...\n`));
61
+ }
62
+ const result = await ctx.preflightService.run({
63
+ tenantId: ctx.tenantId,
64
+ environmentName: ctx.environmentName,
65
+ authMode: ctx.authMode,
66
+ logAnalyticsWorkspaceId: ctx.logAnalyticsWorkspaceId,
67
+ });
68
+ // Auto-save preflight result to history
69
+ const history = new HistoryStore(ctx.configStore.getConfigDir());
70
+ void history.save('preflight', ctx.tenantId, result);
71
+ // Update lastPreflightAt on the tenant profile
72
+ void ctx.configStore.upsertTenant({
73
+ ...ctx.tenant,
74
+ lastPreflightAt: new Date().toISOString(),
75
+ updatedAt: new Date().toISOString(),
76
+ });
77
+ if (ctx.isJson) {
78
+ process.stdout.write(envelopeToJson(createResultEnvelope(result, ctx.tenantId, ctx.environmentName, {
79
+ warnings: result.warnings,
80
+ errors: result.errors,
81
+ })) + '\n');
82
+ }
83
+ else {
84
+ printPreflightTable(result);
85
+ }
86
+ }
87
+ catch (err) {
88
+ handleError(err);
89
+ }
90
+ });
91
+ preflight
92
+ .command('show')
93
+ .description('Show the last cached preflight result (no network call)')
94
+ .action(async () => {
95
+ const opts = program.opts();
96
+ if (!opts.tenant) {
97
+ process.stderr.write('Error: --tenant is required.\n');
98
+ process.exit(1);
99
+ }
100
+ const store = new ConfigStore(opts.configDir);
101
+ const tenant = await store.getTenant(opts.tenant);
102
+ if (!tenant) {
103
+ process.stderr.write(`Error: Tenant "${opts.tenant}" not found.\n`);
104
+ process.exit(1);
105
+ }
106
+ const history = new HistoryStore(store.getConfigDir());
107
+ const cached = await history.loadLatest('preflight', tenant.tenantId);
108
+ if (!cached) {
109
+ process.stderr.write(chalk.dim('No cached preflight result. Run "aarm preflight run --tenant <name>" first.\n'));
110
+ process.exit(1);
111
+ }
112
+ if (opts.output === 'json') {
113
+ process.stdout.write(envelopeToJson(createResultEnvelope(cached, tenant.tenantId, tenant.defaultEnvironmentName ?? 'default')) + '\n');
114
+ }
115
+ else {
116
+ process.stderr.write(chalk.dim(`Showing cached result from ${cached.checkedAt}\n\n`));
117
+ printPreflightTable(cached);
118
+ }
119
+ });
120
+ preflight
121
+ .command('explain')
122
+ .description('List all permissions required and how to grant them')
123
+ .action(async () => {
124
+ const globalOpts = program.opts();
125
+ // Resolve auth mode from stored tenant profile if --tenant is given
126
+ let delegated = false;
127
+ if (globalOpts.tenant) {
128
+ const { ConfigStore } = await import('../config/ConfigStore.js');
129
+ const store = new ConfigStore(globalOpts.configDir);
130
+ const tenant = await store.getTenant(globalOpts.tenant);
131
+ if (tenant)
132
+ delegated = isDelegatedMode(tenant.authMode);
133
+ }
134
+ const showSection = (sectionDelegated) => {
135
+ const label = sectionDelegated
136
+ ? 'Delegated modes (device-code · username-password · interactive-browser · azure-cli)'
137
+ : 'Application modes (client-secret · certificate)';
138
+ process.stdout.write(chalk.bold(`\n${label}\n`));
139
+ process.stdout.write(chalk.dim('─'.repeat(72) + '\n\n'));
140
+ for (const d of PERMISSION_DETAILS) {
141
+ const hint = sectionDelegated ? d.delegatedHint : d.applicationHint;
142
+ const phase = d.mvp ? '' : chalk.dim(' [post-MVP]');
143
+ const consent = d.requiresAdminConsent ? chalk.yellow(' [admin consent]') : '';
144
+ const role = d.requiresUserRole && sectionDelegated ? chalk.magenta(' [user role]') : '';
145
+ process.stdout.write(` ${chalk.cyan(d.capability)}${phase}${consent}${role}\n`);
146
+ process.stdout.write(` ${hint}\n\n`);
147
+ }
148
+ };
149
+ if (globalOpts.tenant) {
150
+ // Show only the relevant mode for the configured tenant
151
+ showSection(delegated);
152
+ }
153
+ else {
154
+ process.stdout.write(chalk.dim('Tip: pass --tenant <name> to see only the hints relevant to your auth mode.\n'));
155
+ showSection(false);
156
+ showSection(true);
157
+ }
158
+ });
159
+ }
160
+ //# sourceMappingURL=preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/commands/preflight.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerReportCommand(program: Command): void;
@@ -0,0 +1,166 @@
1
+ import chalk from 'chalk';
2
+ import { createResultEnvelope, envelopeToJson, riskLevelOrder, } from '@brunsforge/azure-app-registration-monitor';
3
+ import { buildContext, handleError } from '../shared/context.js';
4
+ import { printSecretsTable, secretsToMarkdown, secretsToCsv, } from '../output/formatters.js';
5
+ const RISK_LEVELS = ['Info', 'Low', 'Medium', 'High', 'Critical'];
6
+ function parseRiskLevel(raw) {
7
+ const level = RISK_LEVELS.find((r) => r.toLowerCase() === raw.toLowerCase());
8
+ if (!level) {
9
+ process.stderr.write(`Invalid severity "${raw}". Valid values: ${RISK_LEVELS.map((r) => r.toLowerCase()).join(', ')}\n`);
10
+ process.exit(1);
11
+ }
12
+ return level;
13
+ }
14
+ function allSecrets(inventory) {
15
+ return inventory.flatMap((app) => app.secrets);
16
+ }
17
+ function outputSecrets(secrets, output, tenantId, envName) {
18
+ if (secrets.length === 0) {
19
+ process.stdout.write(chalk.dim('No matching secrets found.\n'));
20
+ return;
21
+ }
22
+ switch (output) {
23
+ case 'json':
24
+ process.stdout.write(envelopeToJson(createResultEnvelope(secrets, tenantId, envName)) + '\n');
25
+ break;
26
+ case 'markdown':
27
+ process.stdout.write(secretsToMarkdown(secrets, tenantId, envName));
28
+ break;
29
+ case 'csv':
30
+ process.stdout.write(secretsToCsv(secrets));
31
+ break;
32
+ default:
33
+ printSecretsTable(secrets);
34
+ }
35
+ }
36
+ export function registerReportCommand(program) {
37
+ const report = program
38
+ .command('report')
39
+ .description('Generate formatted reports from the secret inventory')
40
+ .action(() => report.help());
41
+ report
42
+ .command('expiring')
43
+ .description('Report secrets expiring within a given window')
44
+ .option('--days <n>', 'Look-ahead window in days')
45
+ .option('--months <n>', 'Look-ahead window in months (converted to days × 30)')
46
+ .action(async (cmdOpts) => {
47
+ try {
48
+ const ctx = await buildContext(program.opts());
49
+ const days = cmdOpts.months
50
+ ? parseInt(cmdOpts.months, 10) * 30
51
+ : parseInt(cmdOpts.days ?? '30', 10);
52
+ const inventory = await ctx.inventoryService.getInventory({
53
+ thresholds: { expiringWithinDays: days },
54
+ });
55
+ const expiring = allSecrets(inventory).filter((s) => s.status === 'ExpiringSoon' || s.status === 'Expired');
56
+ outputSecrets(expiring, program.opts().output, ctx.tenantId, ctx.environmentName);
57
+ }
58
+ catch (err) {
59
+ handleError(err);
60
+ }
61
+ });
62
+ report
63
+ .command('tenant-summary')
64
+ .description('Summary of App Registrations and secrets for the tenant')
65
+ .action(async () => {
66
+ try {
67
+ const ctx = await buildContext(program.opts());
68
+ const inventory = await ctx.inventoryService.getInventory();
69
+ const allSec = allSecrets(inventory);
70
+ const summary = {
71
+ tenantId: ctx.tenantId,
72
+ environmentName: ctx.environmentName,
73
+ generatedAt: new Date().toISOString(),
74
+ appCount: inventory.length,
75
+ secretCount: allSec.length,
76
+ expiredCount: allSec.filter((s) => s.status === 'Expired').length,
77
+ expiringIn30Days: allSec.filter((s) => s.daysUntilExpiry !== null && s.daysUntilExpiry >= 0 && s.daysUntilExpiry <= 30).length,
78
+ expiringIn90Days: allSec.filter((s) => s.daysUntilExpiry !== null && s.daysUntilExpiry >= 0 && s.daysUntilExpiry <= 90).length,
79
+ byRisk: Object.fromEntries(RISK_LEVELS.map((r) => [r, allSec.filter((s) => s.riskLevel === r).length])),
80
+ };
81
+ if (ctx.isJson) {
82
+ process.stdout.write(envelopeToJson(createResultEnvelope(summary, ctx.tenantId, ctx.environmentName)) + '\n');
83
+ }
84
+ else {
85
+ process.stdout.write(`\nTenant Summary — ${chalk.bold(ctx.tenantId)}\n`);
86
+ process.stdout.write(`Generated at : ${summary.generatedAt}\n\n`);
87
+ process.stdout.write(`App Registrations : ${summary.appCount}\n`);
88
+ process.stdout.write(`Total secrets : ${summary.secretCount}\n`);
89
+ process.stdout.write(`Expired : ${summary.expiredCount > 0 ? chalk.red(String(summary.expiredCount)) : '0'}\n`);
90
+ process.stdout.write(`Expiring ≤30 days : ${summary.expiringIn30Days > 0 ? chalk.red(String(summary.expiringIn30Days)) : '0'}\n`);
91
+ process.stdout.write(`Expiring ≤90 days : ${summary.expiringIn90Days > 0 ? chalk.yellow(String(summary.expiringIn90Days)) : '0'}\n\n`);
92
+ process.stdout.write('By risk level:\n');
93
+ for (const r of [...RISK_LEVELS].reverse()) {
94
+ const count = summary.byRisk[r];
95
+ if (count > 0)
96
+ process.stdout.write(` ${r.padEnd(8)} ${count}\n`);
97
+ }
98
+ }
99
+ }
100
+ catch (err) {
101
+ handleError(err);
102
+ }
103
+ });
104
+ report
105
+ .command('findings')
106
+ .description('List secrets at or above a given risk level')
107
+ .option('--severity <level>', 'Minimum risk level: info, low, medium, high, critical', 'medium')
108
+ .action(async (cmdOpts) => {
109
+ try {
110
+ const ctx = await buildContext(program.opts());
111
+ const threshold = parseRiskLevel(cmdOpts.severity);
112
+ const thresholdOrder = riskLevelOrder(threshold);
113
+ const inventory = await ctx.inventoryService.getInventory();
114
+ const findings = allSecrets(inventory).filter((s) => riskLevelOrder(s.riskLevel) >= thresholdOrder);
115
+ outputSecrets(findings, program.opts().output, ctx.tenantId, ctx.environmentName);
116
+ }
117
+ catch (err) {
118
+ handleError(err);
119
+ }
120
+ });
121
+ report
122
+ .command('rotation-guide')
123
+ .description('Print a guided rotation checklist for a specific secret')
124
+ .requiredOption('--app-id <client-id>', 'App Registration client ID')
125
+ .requiredOption('--key-id <secret-key-id>', 'Key ID of the secret to rotate')
126
+ .action(async (cmdOpts) => {
127
+ try {
128
+ const ctx = await buildContext(program.opts());
129
+ const now = new Date().toISOString().slice(0, 10);
130
+ const guide = [
131
+ `# Secret Rotation Guide`,
132
+ ``,
133
+ `**Tenant:** ${ctx.tenantId}`,
134
+ `**App ID:** ${cmdOpts.appId}`,
135
+ `**Old Key ID:** ${cmdOpts.keyId}`,
136
+ `**Generated:** ${now}`,
137
+ ``,
138
+ `## Checklist`,
139
+ ``,
140
+ `- [ ] 1. Create a new client secret in the Entra portal for this App Registration.`,
141
+ `- [ ] 2. Store the new secret value securely (OS Credential Manager, Key Vault, or secret manager).`,
142
+ `- [ ] 3. Identify all consumers of this secret (use \`aarm usage analyze --app-id ${cmdOpts.appId}\`).`,
143
+ `- [ ] 4. Update each consumer with the new secret value.`,
144
+ `- [ ] 5. Trigger smoke tests or verify each consumer is working.`,
145
+ `- [ ] 6. Monitor the old key ID for residual usage:`,
146
+ ` \`aarm usage rotation-check --app-id ${cmdOpts.appId} --old-key-id ${cmdOpts.keyId} --days 14\``,
147
+ `- [ ] 7. After 7–14 days with zero usage, delete the old secret in Entra.`,
148
+ ``,
149
+ `## Notes`,
150
+ ``,
151
+ `- Do NOT delete the old secret before step 7 — doing so may break consumers that have not yet been updated.`,
152
+ `- Use \`aarm usage rotation-check\` repeatedly to confirm zero residual usage.`,
153
+ ].join('\n');
154
+ if (ctx.isJson) {
155
+ process.stdout.write(envelopeToJson(createResultEnvelope({ appId: cmdOpts.appId, keyId: cmdOpts.keyId, guide }, ctx.tenantId, ctx.environmentName)) + '\n');
156
+ }
157
+ else {
158
+ process.stdout.write(guide + '\n');
159
+ }
160
+ }
161
+ catch (err) {
162
+ handleError(err);
163
+ }
164
+ });
165
+ }
166
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerSecretsCommand(program: Command): void;
@@ -0,0 +1,88 @@
1
+ import { createResultEnvelope, envelopeToJson, } from '@brunsforge/azure-app-registration-monitor';
2
+ import { buildContext, handleError } from '../shared/context.js';
3
+ import { printSecretsTable } from '../output/formatters.js';
4
+ import { HistoryStore } from '../config/HistoryStore.js';
5
+ function allSecrets(inventory) {
6
+ return inventory.flatMap((app) => app.secrets);
7
+ }
8
+ export function registerSecretsCommand(program) {
9
+ const secrets = program
10
+ .command('secrets')
11
+ .description('Query client secrets across App Registrations')
12
+ .action(() => secrets.help());
13
+ secrets
14
+ .command('list')
15
+ .description('List all secrets')
16
+ .action(async () => {
17
+ try {
18
+ const ctx = await buildContext(program.opts());
19
+ const inventory = await ctx.inventoryService.getInventory();
20
+ const flat = allSecrets(inventory);
21
+ const envelope = createResultEnvelope(flat, ctx.tenantId, ctx.environmentName);
22
+ // Auto-save history — side effect, failures are swallowed inside HistoryStore
23
+ const history = new HistoryStore(ctx.configStore.getConfigDir());
24
+ void history.save('secrets', ctx.tenantId, envelope);
25
+ // Update lastSuccessfulScanAt on the tenant profile
26
+ void ctx.configStore.upsertTenant({
27
+ ...ctx.tenant,
28
+ lastSuccessfulScanAt: new Date().toISOString(),
29
+ updatedAt: new Date().toISOString(),
30
+ });
31
+ if (ctx.isJson) {
32
+ process.stdout.write(envelopeToJson(envelope) + '\n');
33
+ }
34
+ else {
35
+ printSecretsTable(flat);
36
+ }
37
+ }
38
+ catch (err) {
39
+ handleError(err);
40
+ }
41
+ });
42
+ secrets
43
+ .command('expiring')
44
+ .description('List secrets expiring within a given window')
45
+ .option('--days <n>', 'Window in days', '30')
46
+ .option('--months <n>', 'Window in months (converted to days × 30)')
47
+ .action(async (cmdOpts) => {
48
+ try {
49
+ const ctx = await buildContext(program.opts());
50
+ const days = cmdOpts.months
51
+ ? parseInt(cmdOpts.months, 10) * 30
52
+ : parseInt(cmdOpts.days, 10);
53
+ const inventory = await ctx.inventoryService.getInventory({
54
+ thresholds: { expiringWithinDays: days },
55
+ });
56
+ const flat = allSecrets(inventory).filter((s) => s.status === 'ExpiringSoon');
57
+ if (ctx.isJson) {
58
+ process.stdout.write(envelopeToJson(createResultEnvelope(flat, ctx.tenantId, ctx.environmentName)) + '\n');
59
+ }
60
+ else {
61
+ printSecretsTable(flat);
62
+ }
63
+ }
64
+ catch (err) {
65
+ handleError(err);
66
+ }
67
+ });
68
+ secrets
69
+ .command('expired')
70
+ .description('List all expired secrets')
71
+ .action(async () => {
72
+ try {
73
+ const ctx = await buildContext(program.opts());
74
+ const inventory = await ctx.inventoryService.getInventory();
75
+ const flat = allSecrets(inventory).filter((s) => s.status === 'Expired');
76
+ if (ctx.isJson) {
77
+ process.stdout.write(envelopeToJson(createResultEnvelope(flat, ctx.tenantId, ctx.environmentName)) + '\n');
78
+ }
79
+ else {
80
+ printSecretsTable(flat);
81
+ }
82
+ }
83
+ catch (err) {
84
+ handleError(err);
85
+ }
86
+ });
87
+ }
88
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/commands/secrets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerTenantsCommand(program: Command): void;