Skip to content

Commit fd4ce59

Browse files
committed
@W-21461342 Configurable safety levels
1 parent 5a3b351 commit fd4ce59

File tree

33 files changed

+1283
-1
lines changed

33 files changed

+1283
-1
lines changed

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/ci-cd.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,3 +438,42 @@ Logs are always human-readable on stderr. The `--json` flag only controls the st
438438
All actions automatically configure:
439439

440440
- **`NO_COLOR=1`** — clean log output without ANSI colors
441+
442+
## Best Practices
443+
444+
### Use Safety Mode
445+
446+
Enable [Safety Mode](/guide/security#operational-security-safety-mode) in CI/CD to prevent accidental destructive operations:
447+
448+
```yaml
449+
jobs:
450+
deploy:
451+
runs-on: ubuntu-latest
452+
env:
453+
# Prevent accidental deletions
454+
SFCC_SAFETY_LEVEL:NO_DELETE
455+
steps:
456+
- uses: SalesforceCommerceCloud/b2c-developer-tooling/actions/setup@v1
457+
- name: Deploy code
458+
run: b2c code deploy
459+
```
460+
461+
**Safety Levels for CI/CD:**
462+
463+
- **`NO_DELETE`** (Recommended for most CI/CD) - Prevents deletions but allows deployments and updates
464+
- **`NO_UPDATE`** (Strict) - Only allows read and create operations, blocks updates and deletions
465+
- **`READ_ONLY`** (Monitoring/Reporting) - Only allows read operations
466+
467+
**Example: Production deployment with safety:**
468+
```yaml
469+
deploy-production:
470+
environment: production
471+
env:
472+
SFCC_SAFETY_LEVEL: NO_DELETE
473+
steps:
474+
- name: Deploy
475+
run: |
476+
b2c code deploy # ✅ Allowed
477+
b2c sandbox start prod # ✅ Allowed
478+
b2c sandbox delete test # ❌ Blocked by safety mode
479+
```

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: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,167 @@ 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 that prevents accidental or unwanted destructive operations. This is particularly important when:
70+
71+
- Using the CLI in automated environments (CI/CD pipelines)
72+
- Providing the CLI as a tool to AI agents/LLMs
73+
- Working in production environments
74+
- Training new team members
75+
- Running commands from untrusted scripts
76+
77+
### How It Works
78+
79+
Safety Mode uses a **hybrid protection approach**:
80+
81+
1. **HTTP Middleware Layer** (Primary Protection)
82+
- Intercepts ALL HTTP requests before they're sent
83+
- Cannot be bypassed by command-line flags
84+
- Works automatically for all commands
85+
- LLM-proof: controlled via environment variable
86+
87+
2. **Command-Level Checks** (Better UX)
88+
- Provides early, user-friendly error messages
89+
- Catches operations before HTTP requests
90+
91+
### Safety Levels
92+
93+
Configure via the `SFCC_SAFETY_LEVEL` environment variable:
94+
95+
| Level | Description | Blocks |
96+
|-------|-------------|--------|
97+
| `NONE` | No restrictions (default) | Nothing |
98+
| `NO_DELETE` | Prevent deletions | DELETE operations |
99+
| `NO_UPDATE` | Prevent deletions and destructive updates | DELETE + reset/stop/restart |
100+
| `READ_ONLY` | Read-only mode | All writes (POST/PUT/PATCH/DELETE) |
101+
102+
### Usage Examples
103+
104+
#### Development (Allow Everything)
105+
```bash
106+
# No restrictions (default)
107+
unset SFCC_SAFETY_LEVEL
108+
# OR
109+
export SFCC_SAFETY_LEVEL=NONE
110+
111+
b2c sandbox delete test-id # ✅ Allowed
112+
```
113+
114+
#### CI/CD (Prevent Deletions)
115+
```bash
116+
# Prevent accidental deletions in automated environments
117+
export SFCC_SAFETY_LEVEL=NO_DELETE
118+
119+
b2c sandbox create --realm test # ✅ Allowed
120+
b2c sandbox delete test-id # ❌ Blocked
121+
```
122+
123+
#### LLM/Agent Tools (Maximum Protection)
124+
```bash
125+
# Prevent AI agents from performing destructive operations
126+
export SFCC_SAFETY_LEVEL=NO_UPDATE
127+
128+
b2c sandbox list # ✅ Allowed
129+
b2c sandbox create --realm test # ✅ Allowed
130+
b2c sandbox delete test-id # ❌ Blocked
131+
b2c sandbox reset test-id # ❌ Blocked
132+
```
133+
134+
#### Monitoring/Reporting (Read-Only)
135+
```bash
136+
# Absolute read-only mode
137+
export SFCC_SAFETY_LEVEL=READ_ONLY
138+
139+
b2c sandbox list # ✅ Allowed
140+
b2c sandbox get test-id # ✅ Allowed
141+
b2c sandbox create test # ❌ Blocked
142+
```
143+
144+
### What Gets Blocked
145+
146+
| Safety Level | DELETE | POST (create) | POST (reset) | PUT/PATCH | GET |
147+
|--------------|--------|---------------|--------------|-----------|-----|
148+
| **NONE** | ✅ | ✅ | ✅ | ✅ | ✅ |
149+
| **NO_DELETE** | ❌ | ✅ | ✅ | ✅ | ✅ |
150+
| **NO_UPDATE** | ❌ | ✅ | ❌ | ✅ | ✅ |
151+
| **READ_ONLY** | ❌ | ❌ | ❌ | ❌ | ✅ |
152+
153+
### Protected Commands
154+
155+
Safety Mode automatically protects ALL destructive commands across all topics:
156+
157+
- **Sandbox**: `delete`, `reset`, `alias delete`
158+
- **Account Manager**: `users delete`, `users reset`, `clients delete`
159+
- **Code**: `delete`
160+
- **MRT**: `project delete`, `env delete`, `env var delete`, `env redirect delete`, `project notification delete`
161+
- **SLAS**: `client delete`
162+
- **eCDN**: All delete operations (certificates, zones, rules, etc.)
163+
164+
### Why Environment Variable?
165+
166+
Environment variables are more secure than command-line flags because:
167+
168+
1. **LLMs Don't Control Them**: When an LLM uses the CLI as a tool, it controls commands and flags but NOT the environment
169+
2. **Session-Level**: Set once for the entire session/container
170+
3. **Audit Trail**: Can be logged and monitored in CI/CD
171+
4. **Cannot Be Bypassed**: Even `--force` flags don't override safety mode
172+
173+
### Error Messages
174+
175+
When an operation is blocked, you'll see clear error messages:
176+
177+
```bash
178+
export SFCC_SAFETY_LEVEL=NO_DELETE
179+
b2c sandbox delete test-id
180+
181+
# Error: Cannot delete sandbox: blocked by safety level NO_DELETE.
182+
#
183+
# Delete operations are blocked in NO_DELETE mode
184+
#
185+
# To allow this operation, unset or change the SFCC_SAFETY_LEVEL environment variable.
186+
```
187+
188+
### Best Practices
189+
190+
#### For CI/CD Pipelines
191+
```yaml
192+
# GitHub Actions example
193+
- name: Deploy to Production
194+
env:
195+
SFCC_SAFETY_LEVEL: NO_DELETE # Prevent accidental deletions
196+
run: |
197+
b2c code deploy
198+
b2c sandbox start production
199+
```
200+
201+
#### For AI Agent Tools
202+
```bash
203+
# Provide CLI to LLMs with safety enabled
204+
export SFCC_SAFETY_LEVEL=NO_UPDATE
205+
# LLMs can now read and create, but cannot delete or reset
206+
```
207+
208+
#### For Production Environments
209+
```bash
210+
# Set in shell profile for production access
211+
echo 'export SFCC_SAFETY_LEVEL=NO_UPDATE' >> ~/.bashrc
212+
```
213+
214+
### Testing Safety Mode
215+
216+
Verify safety mode is working:
217+
218+
```bash
219+
export SFCC_SAFETY_LEVEL=NO_DELETE
220+
b2c sandbox delete fake-id
221+
222+
# Expected: "blocked by safety level NO_DELETE"
223+
# NOT expected: Authentication error or API call
224+
```
225+
226+
For comprehensive testing, see [GitHub Issue #67](https://github.com/SalesforceCommerceCloud/b2c-developer-tooling/issues/67).
227+
67228
## Best Practices
68229

69230
### For Contributors
@@ -78,3 +239,4 @@ This project uses [NPM trusted publishers](https://docs.npmjs.com/trusted-publis
78239
- Keep the CLI updated to receive security patches
79240
- Review the `pnpm-workspace.yaml` settings if you fork or modify this project
80241
- Consider using similar protections in your own projects
242+
- **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();

0 commit comments

Comments
 (0)