diff --git a/src/commands/audit-log/audit-fixture.json b/src/commands/audit-log/audit-fixture.json new file mode 100644 index 000000000..0757b18ce --- /dev/null +++ b/src/commands/audit-log/audit-fixture.json @@ -0,0 +1,180 @@ +{ + "results": [ + { + "event_id": "123112", + "created_at": "2025-04-02T01:47:26.914Z", + "updated_at": "2025-04-02T01:47:26.914Z", + "country_code": "", + "organization_id": "1381", + "ip_address": "", + "payload": { + "settingKey": "vantaViewSelector", + "settingValue": { + "ignoreAlerts": "", + "ignoreIngress": "" + } + }, + "status_code": 0, + "type": "updateOrganizationSetting", + "user_agent": "", + "user_id": "7d8b2478-abcd-4cc9-abcd-c869de8fc924", + "user_email": "person@socket.dev", + "user_image": "", + "organization_name": "SocketDev" + }, + { + "event_id": "122421", + "created_at": "2025-03-31T15:19:55.299Z", + "updated_at": "2025-03-31T15:19:55.299Z", + "country_code": "", + "organization_id": "1381", + "ip_address": "123.123.321.213", + "payload": { + "name": "zero-access", + "token": "sktsec_...LZEh_api", + "scopes": [] + }, + "status_code": 0, + "type": "createApiToken", + "user_agent": "", + "user_id": "e110f7e0-abcd-41bb-abcd-5745be143db8", + "user_email": "person@socket.dev", + "user_image": "", + "organization_name": "SocketDev" + }, + { + "event_id": "121392", + "created_at": "2025-03-27T16:24:36.344Z", + "updated_at": "2025-03-27T16:24:36.344Z", + "country_code": "", + "organization_id": "1381", + "ip_address": "", + "payload": { + "settingKey": "sso", + "settingValue": { + "defaultMemberRole": "member" + } + }, + "status_code": 0, + "type": "updateOrganizationSetting", + "user_agent": "super ai .com", + "user_id": "6dc7b702-abcd-438a-abcd-51e227962ebd", + "user_email": "person@socket.dev", + "user_image": "", + "organization_name": "SocketDev" + }, + { + "event_id": "121391", + "created_at": "2025-03-27T16:24:33.912Z", + "updated_at": "2025-03-27T16:24:33.912Z", + "country_code": "", + "organization_id": "1381", + "ip_address": "", + "payload": { + "settingKey": "sso", + "settingValue": { + "defaultMemberRole": "member", + "requireSSOOnLogin": true + } + }, + "status_code": 0, + "type": "updateOrganizationSetting", + "user_agent": "", + "user_id": "6dc7b702-abcd-438a-abcd-51e227962ebd", + "user_email": "person@socket.dev", + "user_image": "", + "organization_name": "SocketDev" + }, + { + "event_id": "120287", + "created_at": "2025-03-24T21:52:12.879Z", + "updated_at": "2025-03-24T21:52:12.879Z", + "country_code": "", + "organization_id": "1381", + "ip_address": "", + "payload": { + "alertKey": "Q2URU2WWK6G4jQd3ReRfK-ZUo4xkF_CffmpkhbfgOd3c", + "alertTriageNote": "", + "alertTriageState": null + }, + "status_code": 0, + "type": "updateAlertTriage", + "user_agent": "", + "user_id": "b5d98911-abcd-425b-abcd-c71534f0ef88", + "user_email": "person@socket.dev", + "user_image": "", + "organization_name": "SocketDev" + }, + { + "event_id": "118431", + "created_at": "2025-03-17T15:57:29.885Z", + "updated_at": "2025-03-17T15:57:29.885Z", + "country_code": "", + "organization_id": "1381", + "ip_address": "", + "payload": { + "settingKey": "licensePolicy", + "settingValue": { + "allow": { + "strings": ["0BSD", "ADSL", "AFL-1.1"] + }, + "options": { + "strings": ["toplevelOnly"] + } + } + }, + "status_code": 0, + "type": "updateOrganizationSetting", + "user_agent": "", + "user_id": "7d8b2478-abcd-4cc9-abcd-c869de8fc924", + "user_email": "person@socket.dev", + "user_image": "", + "organization_name": "SocketDev" + }, + { + "event_id": "116928", + "created_at": "2025-03-10T22:53:35.734Z", + "updated_at": "2025-03-10T22:53:35.734Z", + "country_code": "", + "organization_id": "1381", + "ip_address": "", + "payload": { + "token": "sktsec_...wnTa_api", + "scopes": [ + "report", + "repo", + "full-scans", + "packages", + "audit-log", + "integration", + "threat-feed", + "security-policy", + "alerts", + "dependencies", + "historical" + ], + "oldScopes": [ + "report", + "repo", + "full-scans", + "packages", + "audit-log", + "integration", + "threat-feed", + "security-policy", + "alerts", + "dependencies", + "historical" + ] + }, + "status_code": 0, + "type": "updateApiTokenScopes", + "user_agent": "", + "user_id": "1fc4346e-abcd-4537-abcd-113e0e9609b5", + "user_email": "person@socket.dev", + "user_image": "", + "organization_name": "SocketDev" + } + ], + "nextPage": "2" +} diff --git a/src/commands/audit-log/output-audit-log.test.ts b/src/commands/audit-log/output-audit-log.test.ts new file mode 100644 index 000000000..8af8bedc1 --- /dev/null +++ b/src/commands/audit-log/output-audit-log.test.ts @@ -0,0 +1,152 @@ +import { describe, expect, it } from 'vitest' + +import FIXTURE from './audit-fixture.json' with { type: 'json' } +import { outputAsJson, outputAsMarkdown } from './output-audit-log' + +describe('output-audit-log', () => { + describe('json', () => { + it('should return formatted json string', async () => { + const r = await outputAsJson( + JSON.parse(JSON.stringify(FIXTURE.results)), + { + logType: '', + orgSlug: 'noorgslug', + page: 1, + perPage: 10 + } + ) + expect(r).toMatchInlineSnapshot(` + "{ + "desc": "Audit logs for given query", + "generated": "", + "org": "noorgslug", + "logType": "", + "page": 1, + "perPage": 10, + "logs": [ + { + "event_id": "123112", + "created_at": "2025-04-02T01:47:26.914Z", + "ip_address": "", + "type": "updateOrganizationSetting", + "user_agent": "", + "user_email": "person@socket.dev" + }, + { + "event_id": "122421", + "created_at": "2025-03-31T15:19:55.299Z", + "ip_address": "123.123.321.213", + "type": "createApiToken", + "user_agent": "", + "user_email": "person@socket.dev" + }, + { + "event_id": "121392", + "created_at": "2025-03-27T16:24:36.344Z", + "ip_address": "", + "type": "updateOrganizationSetting", + "user_agent": "super ai .com", + "user_email": "person@socket.dev" + }, + { + "event_id": "121391", + "created_at": "2025-03-27T16:24:33.912Z", + "ip_address": "", + "type": "updateOrganizationSetting", + "user_agent": "", + "user_email": "person@socket.dev" + }, + { + "event_id": "120287", + "created_at": "2025-03-24T21:52:12.879Z", + "ip_address": "", + "type": "updateAlertTriage", + "user_agent": "", + "user_email": "person@socket.dev" + }, + { + "event_id": "118431", + "created_at": "2025-03-17T15:57:29.885Z", + "ip_address": "", + "type": "updateOrganizationSetting", + "user_agent": "", + "user_email": "person@socket.dev" + }, + { + "event_id": "116928", + "created_at": "2025-03-10T22:53:35.734Z", + "ip_address": "", + "type": "updateApiTokenScopes", + "user_agent": "", + "user_email": "person@socket.dev" + } + ] + }" + `) + }) + + it('should return empty object string on error', async () => { + const r = await outputAsJson( + {}, + { + logType: '', + orgSlug: 'noorgslug', + page: 1, + perPage: 10 + } + ) + expect(r).toMatchInlineSnapshot(`"{}"`) + }) + }) + + describe('markdown', () => { + it('should return markdown report', async () => { + const r = await outputAsMarkdown( + JSON.parse(JSON.stringify(FIXTURE.results)), + { + logType: '', + orgSlug: 'noorgslug', + page: 1, + perPage: 10 + } + ) + expect(r).toMatchInlineSnapshot(` + " + # Socket Audit Logs + + These are the Socket.dev audit logs as per requested query. + - org: noorgslug + - type filter: (none) + - page: 1 + - per page: 10 + - generated: + + | -------- | ------------------------ | ------------------------- | ----------------- | --------------- | ------------- | + | event_id | created_at | type | user_email | ip_address | user_agent | + | -------- | ------------------------ | ------------------------- | ----------------- | --------------- | ------------- | + | 123112 | 2025-04-02T01:47:26.914Z | updateOrganizationSetting | person@socket.dev | | | + | 122421 | 2025-03-31T15:19:55.299Z | createApiToken | person@socket.dev | 123.123.321.213 | | + | 121392 | 2025-03-27T16:24:36.344Z | updateOrganizationSetting | person@socket.dev | | super ai .com | + | 121391 | 2025-03-27T16:24:33.912Z | updateOrganizationSetting | person@socket.dev | | | + | 120287 | 2025-03-24T21:52:12.879Z | updateAlertTriage | person@socket.dev | | | + | 118431 | 2025-03-17T15:57:29.885Z | updateOrganizationSetting | person@socket.dev | | | + | 116928 | 2025-03-10T22:53:35.734Z | updateApiTokenScopes | person@socket.dev | | | + | -------- | ------------------------ | ------------------------- | ----------------- | --------------- | ------------- | + " + `) + }) + + it('should return empty string on error', async () => { + const r = await outputAsMarkdown( + {}, + { + logType: '', + orgSlug: 'noorgslug', + page: 1, + perPage: 10 + } + ) + expect(r).toMatchInlineSnapshot(`""`) + }) + }) +}) diff --git a/src/commands/audit-log/output-audit-log.ts b/src/commands/audit-log/output-audit-log.ts index 419afd71e..c6bd257bb 100644 --- a/src/commands/audit-log/output-audit-log.ts +++ b/src/commands/audit-log/output-audit-log.ts @@ -1,16 +1,14 @@ -import { stripIndents } from 'common-tags' +import process from 'node:process' +import { debugLog, isDebug } from '@socketsecurity/registry/lib/debug' import { logger } from '@socketsecurity/registry/lib/logger' -import { Separator, select } from '@socketsecurity/registry/lib/prompts' +import constants from '../../constants' import { mdTable } from '../../utils/markdown' -import type { Choice } from '@socketsecurity/registry/lib/prompts' import type { SocketSdkReturnType } from '@socketsecurity/sdk' -type AuditChoice = Choice - -type AuditChoices = Array +const { REDACTED } = constants export async function outputAuditLog( auditLogs: SocketSdkReturnType<'getAuditLogEvents'>['data'], @@ -29,27 +27,46 @@ export async function outputAuditLog( } ): Promise { if (outputKind === 'json') { - await outputAsJson(auditLogs.results, orgSlug, logType, page, perPage) - } else if (outputKind === 'markdown') { - await outputAsMarkdown(auditLogs.results, orgSlug, logType, page, perPage) + logger.log( + await outputAsJson(auditLogs.results, { + logType, + orgSlug, + page, + perPage + }) + ) } else { - await outputAsPrint(auditLogs.results, orgSlug, logType) + logger.log( + await outputAsMarkdown(auditLogs.results, { + logType, + orgSlug, + page, + perPage + }) + ) } } -async function outputAsJson( +export async function outputAsJson( auditLogs: SocketSdkReturnType<'getAuditLogEvents'>['data']['results'], - orgSlug: string, - logType: string, - page: number, - perPage: number -): Promise { + { + logType, + orgSlug, + page, + perPage + }: { + orgSlug: string + page: number + perPage: number + logType: string + } +): Promise { let json try { json = JSON.stringify( { desc: 'Audit logs for given query', - generated: new Date().toISOString(), + generated: process.env['VITEST'] ? REDACTED : new Date().toISOString(), org: orgSlug, logType, page, @@ -82,19 +99,29 @@ async function outputAsJson( logger.fail( 'There was a problem converting the logs to JSON, please try without the `--json` flag' ) - return + if (isDebug()) { + debugLog('Error:', e) + } + return '{}' } - logger.log(json) + return json } export async function outputAsMarkdown( auditLogs: SocketSdkReturnType<'getAuditLogEvents'>['data']['results'], - orgSlug: string, - logType: string, - page: number, - perPage: number -): Promise { + { + logType, + orgSlug, + page, + perPage + }: { + orgSlug: string + page: number + perPage: number + logType: string + } +): Promise { try { const table = mdTable(auditLogs, [ 'event_id', @@ -105,8 +132,7 @@ export async function outputAsMarkdown( 'user_agent' ]) - logger.log( - stripIndents` + return ` # Socket Audit Logs These are the Socket.dev audit logs as per requested query. @@ -114,47 +140,19 @@ These are the Socket.dev audit logs as per requested query. - type filter: ${logType || '(none)'} - page: ${page} - per page: ${perPage} -- generated: ${new Date().toISOString()} +- generated: ${process.env['VITEST'] ? REDACTED : new Date().toISOString()} ${table} ` - ) } catch (e) { process.exitCode = 1 logger.fail( - 'There was a problem converting the logs to JSON, please try without the `--json` flag' + 'There was a problem converting the logs to Markdown, please try the `--json` flag' ) - logger.error(e) - return - } -} - -async function outputAsPrint( - auditLogs: SocketSdkReturnType<'getAuditLogEvents'>['data']['results'], - orgSlug: string, - logType: string -): Promise { - const data: AuditChoices = [] - const logDetails: { [key: string]: string } = {} - - for (const d of auditLogs) { - const { created_at } = d - if (created_at) { - const name = `${new Date(created_at).toLocaleDateString('en-us', { year: 'numeric', month: 'numeric', day: 'numeric' })} - ${d.user_email} - ${d.type} - ${d.ip_address} - ${d.user_agent}` - data.push({ name } as AuditChoice, new Separator()) - logDetails[name] = JSON.stringify(d.payload) + if (isDebug()) { + debugLog('Error:', e) } + // logger.error(e) + return '' } - - logger.log( - logDetails[ - (await select({ - message: logType - ? `\n Audit log for: ${orgSlug} with type: ${logType}\n` - : `\n Audit log for: ${orgSlug}\n`, - choices: data, - pageSize: 30 - })) as any - ] - ) }