Skip to main content
jssg follows a deny-by-default security model for sensitive operations. This means potentially unsafe modules like file system access, network requests, and child processes are disabled by default and must be explicitly enabled.

Why deny-by-default?

Codemods run arbitrary code transformations on your codebase. By restricting capabilities by default, jssg ensures:
  • Safe execution: Untrusted codemods cannot access sensitive resources without explicit permission
  • Audit trail: The capabilities field in codemod.yaml provides a clear record of what permissions a codemod requires
  • Principle of least privilege: Codemods only get the minimum permissions they need

Unsafe modules (require explicit permission)

These modules require explicit opt-in via the capabilities field:

fs

File system accessRead, write, list, and delete files on disk

fetch

Network requestsMake HTTP/HTTPS requests

child_process

Process spawningExecute external commands

Default (safe) modules

These modules are always available and require no special permissions:
  • assert - Assertion testing
  • buffer - Binary data manipulation
  • console - Logging and debugging
  • crypto - Cryptographic operations
  • events - Event emitter patterns
  • os - Operating system information (read-only)
  • path - File path utilities
  • perf_hooks - Performance measurement
  • process - Process information (read-only, no spawning)
  • string_decoder - String encoding/decoding
  • timers - Timer functions
  • tty - TTY operations
  • util - Utility functions
  • stream_web - Web Streams API
  • url - URL parsing and manipulation
  • zlib - Compression/decompression
All default modules are considered safe because they don’t perform file I/O, network requests, or process spawning.

Enabling capabilities

To enable unsafe modules, add a capabilities field to your codemod.yaml file:
codemod.yaml
name: my-codemod
version: 1.0.0
capabilities:
  - fs          # Enable file system access
  - fetch       # Enable network requests
  - child_process  # Enable spawning processes
Only enable capabilities that your codemod actually needs. Each capability increases the potential security risk if the codemod is untrusted.

Capability names

Use these exact strings in the capabilities array:
CapabilityModules EnabledUse Case
fsfs, fs/promisesReading/writing files, checking file existence
fetchfetch globalMaking HTTP requests to APIs
child_processchild_processRunning Git, npm, or other CLI tools

Usage examples

File system operations

1

Enable fs capability

codemod.yaml
capabilities:
  - fs
2

Use fs in your transform

scripts/codemod.ts
import type { Transform } from "codemod:ast-grep";
import { readFileSync } from "fs";

const transform: Transform = (root) => {
  // Read a configuration file
  const config = JSON.parse(readFileSync("./config.json", "utf-8"));
  
  // ... use config in transformation
};

export default transform;

Network requests

1

Enable fetch capability

codemod.yaml
capabilities:
  - fetch
2

Use fetch in your transform

scripts/codemod.ts
import type { Transform } from "codemod:ast-grep";

const transform: Transform = async (root) => {
  // Fetch latest API definitions
  const response = await fetch("https://api.example.com/schema.json");
  const schema = await response.json();
  
  // ... use schema in transformation
};

export default transform;
Note the async transform function when using fetch or other asynchronous operations.

Running external commands

1

Enable child_process capability

codemod.yaml
capabilities:
  - child_process
2

Spawn processes in your transform

scripts/codemod.ts
import type { Transform } from "codemod:ast-grep";
import { execSync } from "child_process";

const transform: Transform = (root) => {
  // Get current git branch
  const branch = execSync("git rev-parse --abbrev-ref HEAD", {
    encoding: "utf-8"
  }).trim();
  
  // ... use branch info in transformation
};

export default transform;
Always validate and sanitize any user input before passing it to execSync or spawn to prevent command injection vulnerabilities.

CLI override flags

You can also enable capabilities via CLI flags when running a codemod:
# Enable specific capabilities
codemod jssg run --allow-fs path/to/codemod
codemod jssg run --allow-fetch --allow-fs path/to/codemod

# Enable all capabilities (use with caution!)
codemod jssg run --allow-fs --allow-fetch --allow-child-process path/to/codemod

# Or with a workflow
codemod workflow run --allow-fs --allow-fetch --allow-child-process -w path/to/workflow.yaml --target path/to/repo
CLI flags take precedence over codemod.yaml. This is useful for testing or one-off runs where you trust the codemod source.

Best practices

Only request capabilities your codemod truly needs. For example, if you only need to read files, document that your codemod won’t write or delete files.
In your README, explain why each capability is required and what operations use it.
README.md
## Required Capabilities

- **fs**: Reads `package.json` to detect project dependencies
- **fetch**: Calls an external API to get data
Never pass unsanitized user input to shell commands:
// ❌ BAD: Command injection vulnerability
const userInput = options.branch;
execSync(`git checkout ${userInput}`);

// ✅ GOOD: Validate input first
const userInput = options.branch;
if (!/^[a-zA-Z0-9_-]+$/.test(userInput)) {
  throw new Error("Invalid branch name");
}
execSync(`git checkout ${userInput}`);
If possible, provide a version of your codemod that works without unsafe capabilities for maximum trust.

Security considerations

Untrusted codemods: Never run codemods from untrusted sources with capabilities enabled without reviewing the code first.

Next steps

I