Skip to content

Allow specifying specific commands that don't need permission to run #3085

Closed

Description

@rben01

What feature would you like to see?

In .claude/settings, users can set explicitly allows commands for Claude to run so that it doesn't need permission. e.g.

{
	"permissions": {
		"allow": [
			"Bash(mkdir:*)",
			"Bash(rg:*)",
			"Bash(npm run build:*)",
			"Bash(grep:*)",
			"Bash(npm run format:*)"
			// ...
		]
	}
}

It's great that Claude Code lets me tell it which tools it doesn't need to ask for permission to use. It would be great if Codex CLI has a similar whitelist of user-specific permissions (not necessarily with the above syntax) so that it would stop repeatedly pestering me to ask if it can run my tests.

Are you interested in implementing this feature?

No response

Additional information

No response

Activity

  1. michaelboeding commented on Sep 16, 2025

    @michaelboeding

    Is this on the roadmap still? Would be very useful

  2. NightMachinery commented on Sep 25, 2025

    @NightMachinery

    I also need this. Codex keeps asking me to approve each curl call.

  3. zwalden commented on Oct 1, 2025

    @zwalden

    Curious about this too.

  4. fcakyon commented on Oct 5, 2025

    @fcakyon

    Is this still not available in Codex? 😩

  5. SwiftedMind commented on Oct 9, 2025

    @SwiftedMind

    This would help me a lot because I can't run xcodebuild inside the sandbox

  6. xshapira commented on Oct 19, 2025

    @xshapira

    We all need this one.

  7. mattcorey commented on Oct 22, 2025

    @mattcorey

    This enhancement is probably my #1 request - there are certain commands that I always want to allow, such as running the scripts to build and test my project (similar to the xcodebuild example from @SwiftedMind a few weeks ago).

    The wildcards are an important aspect to this - tools such as 'axe' (https://github.com/cameroncooke/AXe) would commonly be run over and over again, and provide different parameters each time. Needing to approve each individual combination of parameter is a non-starter, but running on yolo mode is not what I want to do.

  8. l3wi commented on Oct 28, 2025

    @l3wi

    +1 to have this feature. Its so tedious to use codex otherwise

  9. berndeplo commented on Oct 29, 2025

    @berndeplo

    +1

  10. ssimeth commented on Nov 13, 2025

    @ssimeth

    +1

  11. flamerged commented on Nov 13, 2025

    @flamerged

    Why the fuck is there an option to allow a command and then not be asked for permission again, but it simply does not work??

  12. tarmolehtpuu commented on Nov 20, 2025

    @tarmolehtpuu

    Why the fuck is there an option to allow a command and then not be asked for permission again, but it simply does not work??

    Yeah also got surprised by that, but I think it whitelists even the args - so it will only work if the cmd has exactly the same args.

    Came here looking for a way to autoallow gofmt but I am ok reviewing other edits and such. Please consider this feature :)

  13. praxder commented on Nov 25, 2025

    @praxder

    I got really tired of having to always approve this, so I wrote a custom script that allows you to whitelist certain commands. I've been using it for about a month and it's a game changer with Codex CLI. Keep in mind this is my personal script, so you might have to tweak it to get it working right for you, but here's how to set it up:

    Prerequisites:

    • Be on a mac
    • Use iTerm2
    • Have Dart installed
    1. Write a zsh alias for when codex starts (put this in ~/.zshrc):
    alias code='echo "Starting Codex...." && codex && echo "Ending Codex...."'

    this gives us a string to watch for when codex starts and ends.

    1. Put the following script in a file named codex_coprocess.dart (to be used in next step).

    You can obviously edit the approvedCommands list as needed.

    // ignore_for_file: prefer_interpolation_to_compose_strings
    import 'dart:convert';
    import 'dart:io';
    
    void main() {
      showNotification(title: 'Starting', text: 'Starting Codex Coprocess');
    
      ///////////////
      // VARIABLES //
      ///////////////
      const bufferMaxLines = 20;
      final approvedCommands = [
        'cd',
        'curl',
        'dart',
        'echo',
        'export',
        'flutter',
        'gh pr diff',
        'gh pr review',
        'gh pr status',
        'gh pr view',
        'git checkout',
        'git fetch',
        'grep',
        'melos',
        'tail',
        'tee',
        'xcodebuild',
        'xcrun',
      ];
      final actions = [
        (
          RegExp(
            r'Would you like to run the following command\?\n?.*',
            multiLine: true,
            dotAll: true,
          ),
          (final String match) {
            final commandLine = extractCommandLine(match);
    	if (commandLine == null) return;
    
            if (areAllCommandsApproved(commandLine, approvedCommands)) {
              stdout.write('2');
            } else {
              showNotification(
                title: "Pending Request",
                text: 'Tool Needs Approval',
                playSound: true,
              );
            }
          },
        ),
      ];
      ///////////////////
      // END VARIABLES //
      ///////////////////
    
      final buffer = StringBuffer();
      final ansiRegex = RegExp(
        r'(?:\x9B|\x1B\[)[0-?]*[ -/]*[@-~]|\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)|\x1B[PXT^_].*?\x1B\\|\x1B[@-Z\\^_]|\x1B[ -/]*[@-~]',
        dotAll: true,
      );
      final controlCharacterRegex = RegExp(
        r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]',
      );
    
      stdin.transform(utf8.decoder).listen((final input) {
        if (input.contains('Ending Codex....')) {
          showNotification(title: 'Done', text: 'Ended Codex Coprocess');
          exit(0);
        }
    
        final cleanInput = input
            .replaceAll(ansiRegex, '')
            .replaceAll(controlCharacterRegex, '');
        buffer.write(cleanInput);
    
        final numLines = buffer.toString().split('\n').length - 1;
        if (numLines > bufferMaxLines) {
          final bufferWindow = buffer
              .toString()
              .split('\n')
              .sublist(numLines - bufferMaxLines)
              .join('\n');
          buffer
            ..clear()
            ..write(bufferWindow);
        }
    
        for (final (regexp, action) in actions) {
          final match = regexp.stringMatch(buffer.toString());
          if (match != null) {
            action(match);
            buffer.clear();
            break;
          }
        }
      });
    }
    
    /////////////////////////////
    // Command Approval Helpers //
    /////////////////////////////
    
    /// Extracts the command line from a Codex approval prompt match
    String? extractCommandLine(String match) {
      final commandRegex = RegExp(r'\$\s+(.+)$', multiLine: true);
      final commandMatch = commandRegex.firstMatch(match);
      return commandMatch?.group(1)?.trim();
    }
    
    /// Splits a command line into individual commands by &&, ;, and |
    List<String> splitCommands(String commandLine) {
      // Split on &&, ;, or | and trim each segment
      return commandLine
          .split(RegExp(r'&&|;|\|'))
          .map((segment) => segment.trim())
          .where((segment) => segment.isNotEmpty)
          .toList();
    }
    
    /// Extracts the base command from a segment, removing environment variables
    String extractBaseCommand(String segment) {
      // Remove environment variable assignments (KEY=VALUE) from the start
      final envVarRegex = RegExp(r'^(\w+=\S+\s+)+');
      final cleaned = segment.replaceFirst(envVarRegex, '');
      return cleaned.trim();
    }
    
    /// Checks if a command is approved using prefix matching
    bool isCommandApproved(String command, List<String> approvedCommands) {
      return approvedCommands.any((approved) => command.startsWith(approved));
    }
    
    /// Checks if all commands in a chain are approved
    bool areAllCommandsApproved(String commandLine, List<String> approvedCommands) {
      final commands = splitCommands(commandLine);
    
      for (final segment in commands) {
        final baseCommand = extractBaseCommand(segment);
        if (!isCommandApproved(baseCommand, approvedCommands)) {
          return false;
        }
      }
    
      return true;
    }
    
    void showNotification({
      required final String title,
      required final String text,
      final bool playSound = false,
    }) {
      Process.runSync('osascript', [
        '-e',
        'display notification "$text" with title "$title"',
      ]);
      if (playSound) {
        Process.runSync('afplay', ['/System/Library/Sounds/Funk.aiff']);
      }
    }
    
    /////////////////////////
    // Debugging Utilities //
    /////////////////////////
    void log(final String log, {final bool shouldClear = false}) {
      final file = File('/Users/adam/Desktop/log.txt');
      final currentContents = file.existsSync() && !shouldClear
          ? file.readAsStringSync()
          : '';
      file.writeAsStringSync(currentContents + log);
    }
    
    
    1. Create an iTerm2 Coprocess that starts up the custom script when it sees the text "Starting Codex...."
    Image Image

    And the full text for the second screenshot, second part is this (you'll need to update this for your system).

    /Users/adam/Developer/flutter_sdk/bin/dart "/Users/adam/Developer/Scripts/codex_coprocess/lib/codex_coprocess.dart"

    Obviously you will need to update the paths to be relative to your machine.

    It should now be working and when an approvedCommand is requested by codex, it should automatically be approved and continue. This has been a game changer for me.

  14. rimesime-alt commented on Dec 7, 2025

    @rimesime-alt

    +1

  15. marked Command permissions #3960 as a duplicate of this issue on Dec 14, 2025
  16. amit777 commented on Dec 21, 2025

    @amit777

    +1

  17. hivokas commented on Dec 21, 2025

    @hivokas

    Also had this concern, was able to fix it by adding

    prefix_rule(pattern=["eslint"], decision="allow")
    

    to ~/.codex/rules/default.rules.

    Now Codex can run eslint with any arguments without asking me permission.

  18. johan456789 commented on Dec 27, 2025

    @johan456789

    Also had this concern, was able to fix it by adding

    prefix_rule(pattern=["eslint"], decision="allow")
    

    to ~/.codex/rules/default.rules.

    this works quite well. documentation:

    https://developers.openai.com/codex/local-config/#rules-preview

  19. Culpable commented on Dec 30, 2025

    @Culpable

    prefix_rule works for commands with separate argument tokens (e.g. ["eslint"] allows eslint <any args>), but fails when Codex wraps commands as ["/bin/zsh", "-lc", "<entire command>"].

    The third token contains the full heredoc, so there's no way to match just the prefix.

    Example that works:

    # ["eslint", "src/file.js"] — token 0 matches, remaining tokens allowed
    prefix_rule(pattern=["eslint"], decision="allow")

    Example that fails:

    # ["/bin/zsh", "-lc", "cd /path && npx tsx <<'EOF'\n...\nEOF"]
    # Token 2 is the ENTIRE command string including heredoc — no prefix match possible
    prefix_rule(pattern=["/bin/zsh", "-lc", "cd /path && npx tsx"], decision="allow")

    Request: Please add substring/glob support for within-token matching, e.g.:

    prefix_rule(
        pattern=["/bin/zsh", "-lc", startswith("cd /path && npx tsx")],
        decision="allow"
    )
  20. etraut-openai commented on Jan 14, 2026

    @etraut-openai
    Contributor

    This feature is supported through the "rules" mechanism, which is documented here.

  21. fschwahn commented on Jan 14, 2026

    @fschwahn

    @etraut-openai

    Request: Please add substring/glob support for within-token matching

    This does not seem to be possible with the rules mechanism at the moment.

  22. etraut-openai commented on Jan 14, 2026

    @etraut-openai
    Contributor

    @fschwahn, we're looking at the next round of extensions to the existing Rules feature, and we're interested in ideas and feedback. I'd appreciate it if you'd create a new enhancement request and clarify what changes or additions you'd like to see. This thread is quite long (and old) and covers a lot of things that are already supported.

  23. fschwahn commented on Jan 14, 2026

    @fschwahn

    @Culpable you want to open your comment as a new issue? I think it's very clearly written.

  24. aa403 commented on Feb 8, 2026

    @aa403

    this should be reopened -- @etraut-openai the current method doesn't handle wildcards at all as far as i can see. it's super frustrating.

  25. etraut-openai commented on Feb 8, 2026

    @etraut-openai
    Contributor

    @aa403, if you'd like to suggest a new capability, please open a new feature request.

  26. locked as resolved and limited conversation to collaborators on Feb 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsandboxIssues related to permissions or sandboxing

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions