Skip to content

Commit c8a9e77

Browse files
authored
@W-21461342 Adding Configurable safety levels (#235)
* @W-21461342 Configurable safety levels * addressing review comments * removing remnant * addressing review comments
1 parent 0c4e288 commit c8a9e77

32 files changed

Lines changed: 946 additions & 2 deletions

File tree

docs/cli/index.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,32 @@ These flags are available on all commands that interact with B2C instances:
2727
| `--username`, `-u` | `SFCC_USERNAME` | Username for Basic Auth |
2828
| `--password`, `-p` | `SFCC_PASSWORD` | Password/access key for Basic Auth |
2929

30+
### Safety Mode
31+
32+
Safety Mode provides protection against accidental or unwanted destructive operations. This is particularly important when using the CLI in automated environments, CI/CD pipelines, or as a tool for AI agents.
33+
34+
| Environment Variable | Values | Description |
35+
| ---------------------- | ------ | ----------- |
36+
| `SFCC_SAFETY_LEVEL` | `NONE` (default) | No restrictions |
37+
| | `NO_DELETE` | Block DELETE operations |
38+
| | `NO_UPDATE` | Block DELETE and destructive operations (reset, stop, restart) |
39+
| | `READ_ONLY` | Block all write operations (GET only) |
40+
41+
**Example:**
42+
```bash
43+
# Prevent deletions in CI/CD
44+
export SFCC_SAFETY_LEVEL=NO_DELETE
45+
b2c sandbox create --realm test # ✅ Allowed
46+
b2c sandbox delete test-id # ❌ Blocked
47+
48+
# Read-only mode for reporting
49+
export SFCC_SAFETY_LEVEL=READ_ONLY
50+
b2c sandbox list # ✅ Allowed
51+
b2c sandbox create --realm test # ❌ Blocked
52+
```
53+
54+
Safety Mode operates at the HTTP layer and cannot be bypassed by command-line flags. See the [Security Guide](/guide/security#operational-security-safety-mode) for detailed information.
55+
3056
### Other Environment Variables
3157

3258
| Environment Variable | Description |

docs/guide/configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ You can configure the CLI using environment variables:
8282
| `MRT_PROJECT` | MRT project slug (`SFCC_MRT_PROJECT` also supported) |
8383
| `MRT_ENVIRONMENT` | MRT environment name (`SFCC_MRT_ENVIRONMENT`, `MRT_TARGET` also supported) |
8484
| `MRT_CLOUD_ORIGIN` | MRT API origin URL override (`SFCC_MRT_CLOUD_ORIGIN` also supported) |
85+
| `SFCC_SAFETY_LEVEL` | Safety mode: `NONE`, `NO_DELETE`, `NO_UPDATE`, `READ_ONLY` (see [Safety Mode](/guide/security#operational-security-safety-mode)) |
8586

8687
## .env File
8788

docs/guide/security.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,44 @@ When adding a new dependency that requires build scripts:
6464

6565
This project uses [NPM trusted publishers](https://docs.npmjs.com/trusted-publishers) for package publication. Instead of storing long-lived npm tokens, packages are published via GitHub Actions using short-lived OIDC tokens that cannot be extracted or reused.
6666

67+
## Operational Security: Safety Mode
68+
69+
The CLI includes a **Safety Mode** feature via CLI checks and HTTP middleware that prevents accidental or unwanted destructive operations. This is particularly important when:
70+
71+
- Providing the CLI as a tool to AI agents/LLMs
72+
- Working in production environments
73+
- Training new team members
74+
- Running commands from untrusted scripts
75+
76+
### Safety Levels
77+
78+
Configure via the `SFCC_SAFETY_LEVEL` environment variable:
79+
80+
| Level | Description | Blocks |
81+
|-------|-------------|--------|
82+
| `NONE` | No restrictions (default) | Nothing |
83+
| `NO_DELETE` | Prevent deletions | DELETE operations |
84+
| `NO_UPDATE` | Prevent deletions and destructive updates | DELETE + reset/stop/restart |
85+
| `READ_ONLY` | Read-only mode | All writes (POST/PUT/PATCH/DELETE) |
86+
87+
### Usage
88+
89+
```bash
90+
# Default - no restrictions
91+
export SFCC_SAFETY_LEVEL=NONE
92+
93+
# Prevent deletions
94+
export SFCC_SAFETY_LEVEL=NO_DELETE
95+
96+
# Prevent deletions and destructive updates
97+
export SFCC_SAFETY_LEVEL=NO_UPDATE
98+
99+
# Read-only mode
100+
export SFCC_SAFETY_LEVEL=READ_ONLY
101+
```
102+
103+
Environment variables are used instead of command-line flags because LLMs control commands and flags, but not the environment.
104+
67105
## Best Practices
68106

69107
### For Contributors
@@ -78,3 +116,4 @@ This project uses [NPM trusted publishers](https://docs.npmjs.com/trusted-publis
78116
- Keep the CLI updated to receive security patches
79117
- Review the `pnpm-workspace.yaml` settings if you fork or modify this project
80118
- Consider using similar protections in your own projects
119+
- **Use Safety Mode** when running CLI in automated environments or providing it as a tool to AI agents

packages/b2c-cli/src/commands/am/clients/delete.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export default class ClientDelete extends AmCommand<typeof ClientDelete> {
2727
static examples = ['<%= config.bin %> <%= command.id %> <api-client-id>'];
2828

2929
async run(): Promise<void> {
30+
// Prevent deletion in safe mode
31+
this.assertDestructiveOperationAllowed('delete API client');
32+
3033
const apiClientId = this.args['api-client-id'];
3134

3235
this.log(t('commands.client.delete.deleting', 'Deleting API client {{id}}...', {id: apiClientId}));

packages/b2c-cli/src/commands/am/users/delete.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ export default class UserDelete extends AmCommand<typeof UserDelete> {
3535
};
3636

3737
async run(): Promise<void> {
38+
// Prevent deletion in safe mode
39+
this.assertDestructiveOperationAllowed('delete user');
40+
3841
const {login} = this.args;
3942
const {purge} = this.flags;
4043

packages/b2c-cli/src/commands/am/users/reset.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ export default class UserReset extends AmCommand<typeof UserReset> {
2828
];
2929

3030
async run(): Promise<void> {
31+
// Prevent password reset in safe mode
32+
this.assertDestructiveOperationAllowed('reset user password');
33+
3134
const {login} = this.args;
3235

3336
this.log(t('commands.user.reset.fetching', 'Fetching user {{login}}...', {login}));

packages/b2c-cli/src/commands/code/delete.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export default class CodeDelete extends InstanceCommand<typeof CodeDelete> {
6060
};
6161

6262
async run(): Promise<void> {
63+
// Prevent deletion in safe mode
64+
this.assertDestructiveOperationAllowed('delete code version');
65+
6366
this.requireOAuthCredentials();
6467

6568
const codeVersion = this.args.codeVersion;

packages/b2c-cli/src/commands/ecdn/certificates/delete.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ export default class EcdnCertificatesDelete extends EcdnZoneCommand<typeof EcdnC
4949
};
5050

5151
async run(): Promise<DeleteOutput> {
52+
// Prevent deletion in safe mode
53+
this.assertDestructiveOperationAllowed('delete certificate');
54+
5255
this.requireOAuthCredentials();
5356

5457
const zoneId = await this.resolveZoneId();

packages/b2c-cli/src/commands/ecdn/logpush/jobs/delete.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ export default class EcdnLogpushJobsDelete extends EcdnZoneCommand<typeof EcdnLo
3737
};
3838

3939
async run(): Promise<DeleteOutput> {
40+
// Prevent deletion in safe mode
41+
this.assertDestructiveOperationAllowed('delete Logpush job');
42+
4043
this.requireOAuthCredentials();
4144

4245
const zoneId = await this.resolveZoneId();

packages/b2c-cli/src/commands/ecdn/mrt-rules/delete.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export default class EcdnMrtRulesDelete extends EcdnZoneCommand<typeof EcdnMrtRu
3939
};
4040

4141
async run(): Promise<DeleteOutput> {
42+
// Prevent deletion in safe mode
43+
this.assertDestructiveOperationAllowed('delete MRT ruleset');
44+
4245
this.requireOAuthCredentials();
4346

4447
const zoneId = await this.resolveZoneId();

0 commit comments

Comments
 (0)