Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8934b71
feat(rechallenge): add types and error classes
rinatkhaziev Jun 4, 2026
8655df4
feat(rechallenge): add keychain-backed per-scope elevated-token cache
rinatkhaziev Jun 4, 2026
35f9916
feat(rechallenge): add REST client for Parker v2 session endpoints
rinatkhaziev Jun 4, 2026
a6df809
fix(rechallenge): satisfy tsc check-types in test files
rinatkhaziev Jun 4, 2026
b2c46de
feat(rechallenge): orchestrate session create, poll, exchange
rinatkhaziev Jun 4, 2026
3e6cc2f
feat(rechallenge): add Apollo link that intercepts elevated-permissio…
rinatkhaziev Jun 4, 2026
f03c423
feat(api): insert rechallenge link into Apollo chain
rinatkhaziev Jun 4, 2026
a461f52
feat(defensive-mode): add GraphQL helpers for status and config mutat…
rinatkhaziev Jun 4, 2026
b8815af
feat(cli): add vip defensive-mode parent command
rinatkhaziev Jun 4, 2026
20e4e0f
feat(cli): add vip defensive-mode enable subcommand
rinatkhaziev Jun 4, 2026
2dc5804
feat(cli): add vip defensive-mode disable subcommand
rinatkhaziev Jun 4, 2026
1109b2c
feat(cli): add vip defensive-mode configure subcommand
rinatkhaziev Jun 4, 2026
c700b4e
feat(cli): register vip defensive-mode top-level command
rinatkhaziev Jun 4, 2026
80e0d1d
feat(logout): clear elevated-token cache on logout
rinatkhaziev Jun 4, 2026
6763ec4
fix(api): lint cleanup for lazy rechallenge link import
rinatkhaziev Jun 4, 2026
a353924
fix: address final-review findings (diff in configure, non-interactiv…
rinatkhaziev Jun 4, 2026
c9fc8df
fix: address PR review feedback (data guards, version constant, paylo…
rinatkhaziev Jun 4, 2026
5a7ca83
fix: address PR review feedback (output formatting, login hardening, …
rinatkhaziev Jun 10, 2026
8736474
chore(lint): scope no-await-in-loop disable to rechallenge polling loop
rinatkhaziev Jun 10, 2026
3a29646
fix(lint): stop importing Response type from node-fetch in rechalleng…
rinatkhaziev Jun 10, 2026
2750aa4
Merge branch 'trunk' into feat/rechallenge-v2-defensive-mode
rinatkhaziev Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions __tests__/bin/vip-defensive-mode-configure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { describe, expect, it, jest, beforeEach } from '@jest/globals';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test gap: configure production guard behavior is unverified

  • Artifact: __tests__/bin/vip-defensive-mode-configure.js (no production/non-interactive guard case)
  • Category: verification-gap
  • Severity: MEDIUM
  • Confidence: high
  • Classification: Test gap
  • Risk: enable/disable tests assert non-interactive production refusal, but configure has no equivalent test, so the divergence in Finding 1 is unguarded and could regress silently.
  • Evidence: configure test file covers flag parsing/tracking only; no env.type:'production' + nonInteractive:true assertion.
  • Suggested fix: Add a test asserting configure refuses (or requires --skip-confirmation) on production in non-interactive mode once Finding 1 is fixed.


import { defensiveModeConfigureCommand } from '../../src/bin/vip-defensive-mode-configure';
import command from '../../src/lib/cli/command';
import { updateDefensiveModeConfig } from '../../src/lib/defensive-mode/api';
import { trackEvent } from '../../src/lib/tracker';

function mockExit() {
throw 'EXIT';
}
jest.spyOn( console, 'log' ).mockImplementation( () => {} );
jest.spyOn( console, 'error' ).mockImplementation( () => {} );
jest.spyOn( process, 'exit' ).mockImplementation( mockExit );

jest.mock( '../../src/lib/cli/command', () => {
const commandMock = {
argv: () => commandMock,
examples: () => commandMock,
option: () => commandMock,
};
return jest.fn( () => commandMock );
} );

jest.mock( '../../src/lib/defensive-mode/api', () => ( {
updateDefensiveModeConfig: jest.fn( () =>
Promise.resolve( { success: true, message: 'configured' } )
),
appQuery: 'mock-app-query',
} ) );

jest.mock( '../../src/lib/tracker', () => ( {
trackEvent: jest.fn( () => Promise.resolve() ),
} ) );

jest.mock( '../../src/lib/envvar/input', () => ( {
confirm: jest.fn( () => Promise.resolve( true ) ),
} ) );

function baseOpts() {
return {
app: { id: 7, name: 'demo', organization: { id: 1, salesforceId: 'X' } },
env: { id: 9, type: 'develop' },
skipConfirmation: true,
};
}

describe( 'vip defensive-mode configure', () => {
it( 'registers as a command', () => {
expect( command ).toHaveBeenCalled();
} );
} );

describe( 'defensiveModeConfigureCommand', () => {
beforeEach( () => {
jest.clearAllMocks();
} );

it( 'applies full input when all flags are supplied', async () => {
await defensiveModeConfigureCommand( [], {
...baseOpts(),
enabled: 'true',
challengeType: '1',
connectionThresholdAbsolute: '1000',
connectionThresholdPercentage: '50',
} );
expect( updateDefensiveModeConfig ).toHaveBeenCalledWith( {
appId: 7,
envId: 9,
enabled: true,
challengeType: 1,
connectionThresholdAbsolute: 1000,
connectionThresholdPercentage: 50,
} );
} );

it( 'errors when required flags missing in non-interactive mode', async () => {
await expect(
defensiveModeConfigureCommand( [], {
...baseOpts(),
nonInteractive: true,
} )
).rejects.toBe( 'EXIT' );
expect( updateDefensiveModeConfig ).not.toHaveBeenCalled();
} );

it( 'rejects non-boolean enabled values', async () => {
await expect(
defensiveModeConfigureCommand( [], {
...baseOpts(),
enabled: 'maybe',
challengeType: '1',
nonInteractive: true,
} )
).rejects.toBe( 'EXIT' );
} );

it( 'rejects non-integer challenge-type', async () => {
await expect(
defensiveModeConfigureCommand( [], {
...baseOpts(),
enabled: 'true',
challengeType: 'oops',
nonInteractive: true,
} )
).rejects.toBe( 'EXIT' );
} );

it( 'tracks success', async () => {
await defensiveModeConfigureCommand( [], {
...baseOpts(),
enabled: 'false',
challengeType: '1',
} );
expect( trackEvent ).toHaveBeenCalledWith(
'defensive_mode_configure_command_success',
expect.any( Object )
);
} );

it( 'logs the proposed configuration before mutating', async () => {
const consoleSpy = jest.spyOn( console, 'log' );
await defensiveModeConfigureCommand( [], {
...baseOpts(),
enabled: 'true',
challengeType: '2',
} );
const allArgs = consoleSpy.mock.calls.flat().filter( arg => typeof arg === 'string' );
const settingsTable = allArgs.find( arg => arg.includes( 'Challenge type' ) );
expect( settingsTable ).toBeDefined();
expect( settingsTable ).toContain( 'Enabled' );
expect( settingsTable ).toContain( 'true' );
expect( settingsTable ).toContain( '2' );
expect( settingsTable ).toContain( '(not specified)' );
} );

it( 'rejects bare threshold flags (boolean true)', async () => {
await expect(
defensiveModeConfigureCommand( [], {
...baseOpts(),
enabled: 'true',
challengeType: '1',
connectionThresholdAbsolute: true,
nonInteractive: true,
} )
).rejects.toBe( 'EXIT' );
expect( updateDefensiveModeConfig ).not.toHaveBeenCalled();
} );
} );
78 changes: 78 additions & 0 deletions __tests__/bin/vip-defensive-mode-disable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { describe, expect, it, jest, beforeEach } from '@jest/globals';

import { defensiveModeDisableCommand } from '../../src/bin/vip-defensive-mode-disable';
import command from '../../src/lib/cli/command';
import { updateDefensiveModeStatus } from '../../src/lib/defensive-mode/api';
import { trackEvent } from '../../src/lib/tracker';

function mockExit() {
throw 'EXIT';
}
jest.spyOn( console, 'log' ).mockImplementation( () => {} );
jest.spyOn( console, 'error' ).mockImplementation( () => {} );
jest.spyOn( process, 'exit' ).mockImplementation( mockExit );

jest.mock( '../../src/lib/cli/command', () => {
const commandMock = {
argv: () => commandMock,
examples: () => commandMock,
option: () => commandMock,
};
return jest.fn( () => commandMock );
} );

jest.mock( '../../src/lib/defensive-mode/api', () => ( {
updateDefensiveModeStatus: jest.fn( () =>
Promise.resolve( { success: true, message: 'disabled' } )
),
appQuery: 'mock-app-query',
} ) );

jest.mock( '../../src/lib/tracker', () => ( {
trackEvent: jest.fn( () => Promise.resolve() ),
} ) );

describe( 'vip defensive-mode disable', () => {
it( 'registers as a command', () => {
expect( command ).toHaveBeenCalled();
} );
} );

describe( 'defensiveModeDisableCommand', () => {
beforeEach( () => {
jest.clearAllMocks();
} );

it( 'calls updateDefensiveModeStatus with enabled=false', async () => {
const opts = {
app: { id: 7, name: 'demo', organization: { id: 1, salesforceId: 'X' } },
env: { id: 9, type: 'develop' },
skipConfirmation: true,
};
await defensiveModeDisableCommand( [], opts );
expect( updateDefensiveModeStatus ).toHaveBeenCalledWith( {
appId: 7,
envId: 9,
enabled: false,
} );
expect( trackEvent ).toHaveBeenCalledWith(
'defensive_mode_disable_command_success',
expect.any( Object )
);
} );

it( 'exits with error on production without skip-confirmation in non-interactive mode', async () => {
const opts = {
app: { id: 7, name: 'demo', organization: { id: 1, salesforceId: 'X' } },
env: { id: 9, type: 'production' },
skipConfirmation: false,
nonInteractive: true,
};
await expect( defensiveModeDisableCommand( [], opts ) ).rejects.toBe( 'EXIT' );
expect( updateDefensiveModeStatus ).not.toHaveBeenCalled();
expect( trackEvent ).toHaveBeenCalledWith(
'defensive_mode_disable_command_cancelled',
expect.any( Object )
);
} );
} );
85 changes: 85 additions & 0 deletions __tests__/bin/vip-defensive-mode-enable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { describe, expect, it, jest, beforeEach } from '@jest/globals';

import { defensiveModeEnableCommand } from '../../src/bin/vip-defensive-mode-enable';
import command from '../../src/lib/cli/command';
import { updateDefensiveModeStatus } from '../../src/lib/defensive-mode/api';
import { trackEvent } from '../../src/lib/tracker';

function mockExit() {
throw 'EXIT';
}
jest.spyOn( console, 'log' ).mockImplementation( () => {} );
jest.spyOn( console, 'error' ).mockImplementation( () => {} );
jest.spyOn( process, 'exit' ).mockImplementation( mockExit );

jest.mock( '../../src/lib/cli/command', () => {
const commandMock = {
argv: () => commandMock,
examples: () => commandMock,
option: () => commandMock,
};
return jest.fn( () => commandMock );
} );

jest.mock( '../../src/lib/defensive-mode/api', () => ( {
updateDefensiveModeStatus: jest.fn( () =>
Promise.resolve( { success: true, message: 'enabled' } )
),
appQuery: 'mock-app-query',
} ) );

jest.mock( '../../src/lib/tracker', () => ( {
trackEvent: jest.fn( () => Promise.resolve() ),
} ) );

const mockUpdate = updateDefensiveModeStatus;
const mockTrack = trackEvent;

describe( 'vip defensive-mode enable', () => {
it( 'registers as a command', () => {
expect( command ).toHaveBeenCalled();
} );
} );

describe( 'defensiveModeEnableCommand', () => {
beforeEach( () => {
jest.clearAllMocks();
} );

it( 'calls updateDefensiveModeStatus with enabled=true', async () => {
const opts = {
app: { id: 7, name: 'demo', organization: { id: 1, salesforceId: 'X' } },
env: { id: 9, type: 'develop' },
skipConfirmation: true,
};
await defensiveModeEnableCommand( [], opts );
expect( mockUpdate ).toHaveBeenCalledWith( {
appId: 7,
envId: 9,
enabled: true,
} );
expect( mockTrack ).toHaveBeenCalledWith(
'defensive_mode_enable_command_execute',
expect.any( Object )
);
expect( mockTrack ).toHaveBeenCalledWith(
'defensive_mode_enable_command_success',
expect.any( Object )
);
} );

it( 'exits with error on production without skip-confirmation in non-interactive mode', async () => {
const opts = {
app: { id: 7, name: 'demo', organization: { id: 1, salesforceId: 'X' } },
env: { id: 9, type: 'production' },
skipConfirmation: false,
nonInteractive: true,
};
await expect( defensiveModeEnableCommand( [], opts ) ).rejects.toBe( 'EXIT' );
expect( mockUpdate ).not.toHaveBeenCalled();
expect( mockTrack ).toHaveBeenCalledWith(
'defensive_mode_enable_command_cancelled',
expect.any( Object )
);
} );
} );
Loading
Loading