107 lines
3.4 KiB
Markdown
107 lines
3.4 KiB
Markdown
# External Directory Permission Auto-Deny Implementation
|
|
|
|
## Summary
|
|
Automatically denies OpenCode `external_directory` permission requests when they don't match the current app's ID, preventing permission prompts from appearing in the builder UI.
|
|
|
|
## Storage Impact
|
|
- **Config file size**: ~208 bytes per app
|
|
- **Location**: `{workspaceDir}/opencode.json` (in each app's workspace directory)
|
|
- **Overhead**: Negligible (< 0.3 KB per app)
|
|
|
|
## Implementation Details
|
|
|
|
### Files Modified
|
|
|
|
1. **chat/server.js**:
|
|
- Added `ENABLE_EXTERNAL_DIR_RESTRICTION` environment variable (line 102)
|
|
- Added `ensureOpencodeConfig(session)` function (lines 1960-2001)
|
|
- Modified `ensureSessionPaths(session)` to call the new function (line 1956)
|
|
|
|
2. **.env.example**:
|
|
- Added `ENABLE_EXTERNAL_DIR_RESTRICTION` documentation (line 35)
|
|
|
|
### How It Works
|
|
|
|
1. When a session is created, `ensureSessionPaths()` is called
|
|
2. This function calls `ensureOpencodeConfig()` which:
|
|
- Extracts the app ID and user ID from the session
|
|
- Creates an `opencode.json` config file in the workspace directory
|
|
- Configures `external_directory` permission rules:
|
|
- Deny all external directory access (`*`: "deny")
|
|
- Allow access only to current app's paths:
|
|
- `*/{appId}/*` - Any path containing the app ID
|
|
- `apps/{userId}/{appId}/*` - The full workspace path pattern
|
|
|
|
3. OpenCode automatically loads this config when running in the workspace directory
|
|
4. Permission requests for paths matching the current app ID are auto-allowed
|
|
5. Permission requests for other apps are auto-denied (no user prompt)
|
|
|
|
### Example Config File
|
|
|
|
```json
|
|
{
|
|
"$schema": "https://opencode.ai/config.json",
|
|
"permission": {
|
|
"external_directory": {
|
|
"*": "deny",
|
|
"*/c7f9e5c6-e7c2-4258-a583-ccffcf9791c8/*": "allow",
|
|
"apps/user123/c7f9e5c6-e7c2-4258-a583-ccffcf9791c8/*": "allow"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Environment Variable
|
|
|
|
```
|
|
ENABLE_EXTERNAL_DIR_RESTRICTION=1 # Default: enabled
|
|
```
|
|
|
|
To disable the feature:
|
|
```
|
|
ENABLE_EXTERNAL_DIR_RESTRICTION=false
|
|
```
|
|
|
|
### Usage
|
|
|
|
1. **New sessions**: Config is automatically created when session starts
|
|
2. **Existing sessions**: Config is created next time `ensureSessionPaths()` runs
|
|
3. **Failed writes**: Logged but doesn't block session creation
|
|
|
|
## Security Benefits
|
|
|
|
1. **App isolation**: Prevents one app from accessing another app's files
|
|
2. **No user prompts**: Permission requests are handled automatically
|
|
3. **Minimal exposure**: Only allows access to current app's workspace directory
|
|
4. **Fail-safe**: If config creation fails, session continues (logs error)
|
|
|
|
## Testing Checklist
|
|
|
|
- [ ] Verify config file created in workspace directory
|
|
- [ ] Confirm OpenCode loads config (no permission prompts for same app)
|
|
- [ ] Test access to different app ID → auto-denied
|
|
- [ ] Verify no permission dialogs appear in builder UI
|
|
- [ ] Test with both UUID and slug app IDs
|
|
- [ ] Test with anonymous users
|
|
- [ ] Verify `ENABLE_EXTERNAL_DIR_RESTRICTION=false` disables feature
|
|
- [ ] Check that config file is ~200-300 bytes (minimal storage)
|
|
|
|
## Logs
|
|
|
|
On successful creation:
|
|
```
|
|
Created opencode config for session
|
|
sessionId: c7f9e5c6-e7c2-4258-a583-ccffcf9791c8
|
|
appId: my-shopify-app
|
|
userId: user123
|
|
```
|
|
|
|
On failure (non-blocking):
|
|
```
|
|
Failed to create opencode config
|
|
sessionId: c7f9e5c6-e7c2-4258-a583-ccffcf9791c8
|
|
error: EACCES: permission denied
|
|
```
|