Skip to content

Add Defensive Mode to VIP-CLI#2756

Open
Crixu wants to merge 12 commits intotrunkfrom
add/defensive-mode-cli-commands
Open

Add Defensive Mode to VIP-CLI#2756
Crixu wants to merge 12 commits intotrunkfrom
add/defensive-mode-cli-commands

Conversation

@Crixu
Copy link

@Crixu Crixu commented Mar 4, 2026

Description

This PR adds CLI commands for managing Defensive Mode (bot/DDoS protection) on VIP environments. Customers can now automate defensive mode operations via vip-cli instead of manually managing each environment through the Dashboard.

Commands Added

  • vip @app.env defensive-mode enable - Enable bot/DDoS protection
  • vip @app.env defensive-mode disable - Disable protection (with confirmation)
  • vip @app.env defensive-mode status - View current configuration

Implementation Details

API Integration: Uses GraphQL API (not REST) via existing VIP-CLI patterns

  • Query: app.environments.defensiveMode for status
  • Mutations: updateDefensiveModeStatus for enable/disable

Security Features:

  • Interactive confirmation required for disable (destructive operation)
  • --confirm flag for automation (non-interactive mode)
  • Permission checks enforced by API (Org Admin or App Admin required)
  • All operations tracked via analytics events

Changelog Description

Added

  • Added vip defensive-mode enable command to enable bot and DDoS protection for an environment
  • Added vip defensive-mode disable command to disable bot and DDoS protection with confirmation prompt
  • Added vip defensive-mode status command to view current Defensive Mode configuration and state

Pull Request Checklist


New Release Checklist


Steps to Test

Prerequisites

  • Access to VIP app with Defensive Mode available
  • Org Admin or App Admin role

Test Enable Command

  1. Check out PR and build:

    git checkout add/defensive-mode-cli-commands
    npm run build
  2. Check initial status (should show current state):

    ./dist/bin/vip.js @your-app.production defensive-mode status
  3. Enable defensive mode:

    ./dist/bin/vip.js @your-app.production defensive-mode enable
  4. Verify:

    • Success message displays
    • Status shows "ACTIVE"
    • Configuration details displayed (threshold, challenge type, etc.)
    • Dashboard shows defensive mode enabled

Test Disable Command (Interactive)

  1. Disable with confirmation prompt:

    ./dist/bin/vip.js @your-app.production defensive-mode disable
  2. Verify:

    • Warning message displays with app/env name and domain
    • Prompt requires typing "DISABLE"
    • After confirmation, status shows "INACTIVE"
    • Dashboard shows defensive mode disabled
  3. Test cancellation:

    ./dist/bin/vip.js @your-app.production defensive-mode disable
    • Type anything other than "DISABLE"
    • Verify operation cancelled, defensive mode still disabled

Test Disable Command (Non-Interactive)

  1. Re-enable first:

    ./dist/bin/vip.js @your-app.production defensive-mode enable
  2. Disable with --confirm flag:

    ./dist/bin/vip.js @your-app.production defensive-mode disable --confirm
  3. Verify:

    • No prompt shown
    • Defensive mode disabled immediately
    • Success message displays

Test JSON Output

  1. Enable with JSON output:

    ./dist/bin/vip.js @your-app.production defensive-mode enable --format=json
  2. Verify:

    • Valid JSON output
    • Contains data.effective and data.stored objects
    • No human-readable text mixed in

Test Status Display

  1. Check status on WordPress site:

    ./dist/bin/vip.js @wordpress-app.production defensive-mode status
    • Verify shows "X% PHP workers" (percentage threshold)
  2. Check status on Node.js site (if available):

    ./dist/bin/vip.js @nodejs-app.production defensive-mode status
    • Verify shows "X concurrent requests" (absolute threshold)

Test Error Handling

  1. Test with insufficient permissions (if possible):

    • Use account with App Write or lower role
    • Verify clear error message about required role
  2. Test with invalid app:

    ./dist/bin/vip.js @nonexistent-app.production defensive-mode status
    • Verify appropriate error message

Verify Tests

npm test -- --testPathPatterns=defensive-mode

Expected: 24 tests passing

Crixu added 7 commits March 3, 2026 15:40
Implements defensive-mode subcommands for VIP-CLI:
- vip @app.env defensive-mode enable: Enable bot/DDoS protection
- vip @app.env defensive-mode disable: Disable protection with confirmation
- vip @app.env defensive-mode status: Display current config and status

Features:
- REST API integration via existing http client
- JSON output format for automation (--format=json)
- Interactive confirmation prompt for disable (--confirm to skip)
- Permission-aware error messages
- Analytics tracking via trackEvent
- Follows existing VIP-CLI patterns and conventions

Related: VIP CLI-Dashboard Parity Initiative (24 feature gaps identified)
Tests cover:
- Enable command: success cases, JSON output, already enabled state, error handling
- Disable command: confirmation prompt, --confirm flag, cancellation, error handling
- Status command: active/inactive states, WordPress/Node.js thresholds, custom vs default values, JSON output

All 24 tests passing:
- 8 enable command tests
- 8 disable command tests
- 8 status command tests

Includes proper mocking of API, exit, tracker, and prompt modules.
Test Plan (test-plan-defensive-mode.md):
- 20 comprehensive test cases
- Prerequisites and environment setup
- Manual validation procedures
- Dashboard verification steps
- Bulk operations testing
- Sign-off checklist

User Documentation (docs/defensive-mode.md):
- Complete CLI reference for all 3 commands
- Configuration field explanations
- Bulk operations examples
- CI/CD integration examples
- Best practices and troubleshooting
- Error message reference
- Permission requirements

Addresses steps 3 (staging validation) and 4 (documentation).
Add defensive-mode to command registry in vip.js to make it accessible via:
vip @app.env defensive-mode <subcommand>

Placed alphabetically between 'db' and 'dev-env' commands.
Issue: REST endpoints (/v1/sites/:siteId/defensive-mode) return 404
Cause: REST API not deployed to production Parker yet

Solution: Use GraphQL queries/mutations instead (what Dashboard uses)

Changes:
- getDefensiveMode: Uses GraphQL query or appQuery data
- updateDefensiveMode: Uses GraphQL mutation
- Expanded appQuery to include defensiveMode.config fields
- Fixed status display: correct env name, show only active threshold
- Tested successfully on vip-lucas-radke production (6633)

Result: All commands now working against production API ✅
- Use updateDefensiveModeStatus mutation for enable/disable (not updateDefensiveModeConfig)
- Query for updated config after mutation (mutation only returns success/message)
- Fix environment name display in all commands (use type instead of name)
- Fix threshold display to only show non-null value (percentage OR absolute, not both)

Tested successfully:
- Disable: vip-lucas-radke production -> INACTIVE ✅
- Enable: vip-lucas-radke production -> ACTIVE ✅
- Status: Shows correct config with proper formatting ✅

All 3 commands working end-to-end on production.
Purpose and Context:
Fixed TypeScript linting errors related to unsafe 'any' type access
on GraphQL responses. Added proper type definitions for all API
responses and parameters.

Key Changes:
- Added DefensiveModeGraphQLConfig interface
- Added UpdateDefensiveModeConfigResponse interface
- Added UpdateDefensiveModeStatusResponse interface
- Added DefensiveModeQueryResponse interface
- Added EnvironmentData interface for envData parameter
- Added type parameters to api.query() and api.mutate() calls
- Added null checks for message.includes() calls

Impact and Considerations:
Improves type safety and IDE autocomplete support. No runtime
behavior changes. Reduces linting errors from 31 to 4 (warnings only).

Testing and Validation:
- Build successful
- All 24 tests still passing
- Linting errors resolved (0 errors, 4 warnings)
@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Crixu and others added 5 commits March 4, 2026 07:06
- Update test to expect third parameter (envData) in getDefensiveMode call
- Fix Prettier formatting in docs/defensive-mode.md

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Prefix unused parameters with underscore to comply with no-unused-vars rule

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove redundant error message fallbacks that confused static analysis

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove redundant null checks (numbers can't be null in TypeScript)
- Extract common status update logic to reduce duplication from 16.8% to <3%
- Simplify enable/disable to use shared helper function

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 4, 2026

@Crixu Crixu requested a review from a team March 4, 2026 13:48
@sjinks sjinks requested a review from Copilot March 9, 2026 04:43
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds CLI commands for managing Defensive Mode (bot/DDoS protection) on VIP environments. It introduces three new subcommands under vip defensive-modeenable, disable, and status — following the established VIP-CLI command patterns (appQuery, trackEvent, command registration). The implementation uses GraphQL mutations for enable/disable and queries for status, with JSON output support for automation.

Changes:

  • Added vip defensive-mode enable/disable/status commands with GraphQL API integration, tracking events, and both table and JSON output formats
  • Added interactive confirmation prompt for the disable command (destructive operation) with a --confirm flag for automation
  • Added comprehensive documentation and 24 unit tests covering all three commands

Reviewed changes

Copilot reviewed 3 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/lib/api/defensive-mode.ts New API module with GraphQL queries/mutations for defensive mode operations
src/bin/vip-defensive-mode.js Parent command registering enable/disable/status subcommands
src/bin/vip-defensive-mode-enable.js Enable command with tracking and JSON/table output
src/bin/vip-defensive-mode-disable.js Disable command with confirmation prompt and tracking
src/bin/vip-defensive-mode-status.js Status command displaying configuration details
src/bin/vip.js Registers defensive-mode command and sorts db alphabetically
package.json Adds bin entries for the four new command files
docs/defensive-mode.md Comprehensive documentation with examples, CI/CD integration, and troubleshooting
__tests__/bin/vip-defensive-mode-enable.js Tests for enable command
__tests__/bin/vip-defensive-mode-disable.js Tests for disable command
__tests__/bin/vip-defensive-mode-status.js Tests for status command

# Defensive Mode CLI Commands

**Commands**: `vip defensive-mode enable`, `vip defensive-mode disable`, `vip defensive-mode status`
**Available in**: VIP-CLI 3.23.0+
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The documentation states "Available in: VIP-CLI 3.23.0+" but the current package.json version is 3.25.1-dev.0, meaning this feature will ship in version 3.25.1 or later. The version number should be updated to match the actual release version.

Suggested change
**Available in**: VIP-CLI 3.23.0+
**Available in**: VIP-CLI 3.25.1+

Copilot uses AI. Check for mistakes.

## Changelog

### 3.23.0 (2026-03-03)
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The changelog references version "3.23.0 (2026-03-03)" but the current package.json version is 3.25.1-dev.0, so this feature will ship in a later version. This should be updated to match the actual release version.

Copilot uses AI. Check for mistakes.
Comment on lines +394 to +398

Suggestions:
1. Verify your network connection
2. Try again in a few moments
3. Check VIP status: https://status.wpvip.com/
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The error message examples in the documentation don't match what the code actually produces. For instance, the "Permission Denied" example shows "Your current role: App Write" and "To resolve: Contact your organization admin..." but the actual code (src/lib/api/defensive-mode.ts:204-206) only produces: "Insufficient permissions to manage Defensive Mode. Required role: Org Admin or App Admin". Similarly, the "Network or API Error" example shows a "Suggestions:" block that the code never produces. These examples should match the actual error output to avoid confusing users.

Suggested change
Suggestions:
1. Verify your network connection
2. Try again in a few moments
3. Check VIP status: https://status.wpvip.com/

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +90
- `1` - General error (API failure, network issue)
- `2` - Permission denied

---

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The documentation claims exit code 2 for "Permission denied" but the code always uses exit.withError() which calls process.exit(1) for all error conditions including permission errors. There is no code path that produces exit code 2. Either update the docs to only document exit code 1 for all errors, or implement distinct exit codes in the code.

Suggested change
- `1` - General error (API failure, network issue)
- `2` - Permission denied
---
- `1` - Error (API failure, network issue, permission denied, etc.)
---

Copilot uses AI. Check for mistakes.
Comment on lines +138 to +143
**Exit Codes**:

- `0` - Success or cancelled by user
- `1` - General error
- `2` - Permission denied

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

Same issue as in the enable section: exit code 2 for "Permission denied" is documented but never produced by the code. exit.withError() always exits with code 1.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants