NAME

Langertha::Raider::Permissions::Rule - Internal permission rule in Claude Code syntax, parsed and matched against tool calls

VERSION

version 0.504

SYNOPSIS

# Internal to Langertha-Raider -- no API promise.
my $rule = Langertha::Raider::Permissions::Rule->new(
  text   => 'Bash(npm run test:*)',
  source => '.claude/settings.json',
  kind   => 'allow',
);
warn $rule->error unless $rule->is_valid;
my $yes = $rule->matches( { name => 'bash', source => 'engine:2',
  arguments => { command => 'npm run test' } }, $ctx );

DESCRIPTION

Internal module. Its interface may change without notice.

One permission rule as Claude Code writes it in permissions.allow, ask or deny (ADR 0021), with the source it came from. The rule is parsed once on construction; a rule that cannot be parsed, or that names a tool raider does not know, is kept with an "error" and never matches -- an invalid rule never widens anything, and config explain shows it.

The forms:

Tool -- every call of the tool. Claude Code names go through Langertha::Raider::ToolNames; raider names (write_file) are accepted as well. Read stands for every reading file tool (read_file, list_files, glob, grep), Edit / Write / MultiEdit for every writing one (write_file, edit_file, multi_edit).
Bash(command) exact, Bash(prefix:*) prefix at a word boundary, Bash(git * --dry-run) glob (* is any text). The command is split into its parts at &&, ||, ;, |, &, newlines and parentheses, outside quotes, with $(...), backticks and <(...) / >(...) searched as commands of their own. A deny or ask rule matches when any part matches (also with leading keywords, wrappers such as sudo and VAR=value assignments taken off). An allow rule covers only a command of one part that matches as written (or, for an exact rule, the whole command as written), without substitutions, heredocs or redirections into files; Langertha::Raider::Permissions covers a command of several parts when every part is covered by some allow rule. A Bash rule applies to the monitor tool as well (its command runs the same way, ADR 0031); Monitor and Monitor(command) name the monitor alone.
C<Read(path)>, C<Edit(path)> -- gitignore-style globs (C<*>, C<**>,
C<?>). C<//abs> is absolute, C<~/x> under the home, C</x>, C<./x> and
C<x/y> under the project root, and a pattern without a slash matches at any
depth under the root. A pattern that matches a directory matches everything
below it. The path of the call is resolved like the file tools resolve it
(relative to the root, C<..> taken out, symbolic links followed) and both
the written and the real form are tested: a deny or ask rule matches when
either does, an allow rule only when both do. For C<glob> and C<grep> the
path is the search root: a rule that names it (or a directory above it)
matches the call; the files below it that a rule names are left out of the
results by the search tools themselves (L<Langertha::Raider::SearchTools/hider>).
Agent(name) / Task(name) -- an agent call whose subagent_type is name (a call without one starts general-purpose, so it counts as that, ADR 0020); Skill(name) -- a skill call for the skill name. Exact names.
WebFetch(domain:example.com) -- the host of url; domain:*.example.com matches subdomains.
mcp__server, mcp__server__*, mcp__server__tool -- tools of an MCP server that is not raider's own (the server name comes with the call context). The tool part is compared with the call's name, or, for a configured server whose tools are called mcp__server__tool (ADR 0029), with the part after that prefix.

A rule that names a raider tool by name (not mcp__) applies to a tool of that name from a foreign MCP server too -- but only as deny or ask: an allow rule for Bash allows raider's bash, never another server's.

text

The rule as written. Required.

source

Where the rule came from (a settings file, .raider/config.yml, session, ...). Required.

kind

allow, ask or deny. Required; croaks on anything else.

is_valid

True when the rule could be parsed and names a known tool.

error

Why the rule is invalid, or undef.

family

tool, bash, path, domain, named or mcp; undef for an invalid rule.

tools

The raider tools the rule applies to (a new array ref; empty for an MCP rule or an invalid one).

specifier

The text inside the parentheses, or undef for a bare tool rule.

mcp_server

mcp_tool

For an MCP rule: the server name, and the tool name (undef: every tool of the server).

as_hash

{ rule, source, kind, error? } for reports.

matches

my $yes = $rule->matches( $call, $ctx );

True when the rule applies to the canonical call { name, source, arguments } (see _tool_gate in Langertha::Raider). $ctx is the context of the call, built by Langertha::Raider::Permissions: roots and homes (the project root and the home, each as written and as real path), builtin (true for a tool of raider's own) and server (the MCP server of a foreign tool). An invalid rule never matches.

covers_command_part

my $yes = $rule->covers_command_part('npm run test');

For a Bash rule: true when the rule matches one part of a command as written (see "shell_parts"). A bare Bash rule covers every part.

shell_parts

my $shell = $rule->shell_parts('npm test && git push');
# { parts => [ 'npm test', 'git push' ], opaque => 0 }

The parts of a shell command, split as described above, and opaque: true when the command holds a command or process substitution, a heredoc, a redirection into a file (not /dev/null, not >&2) or unbalanced quotes -- a command no allow rule with a specifier covers. Callable on the class.

call_paths

my @paths = $rule->call_paths( 'lib/../secrets/key', $ctx );

The absolute forms of a path argument a rule is held against: resolved against the project root with . and .. taken out, and its real path (symbolic links of its nearest existing ancestor followed) when that differs. Callable on the class.

SEE ALSO

SUPPORT

Issues

Please report bugs and feature requests at https://github.com/Getty/langertha-raider/issues.

IRC

Join #langertha on irc.perl.org or message Getty directly.

CONTRIBUTING

Contributions are welcome! Please fork the repository and submit a pull request.

AUTHOR

Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Torsten Raudssus.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

https://raudssus.de/