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.Readstands for every reading file tool (read_file,list_files,glob,grep),Edit/Write/MultiEditfor 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 assudoandVAR=valueassignments 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. ABashrule applies to themonitortool as well (its command runs the same way, ADR 0031);MonitorandMonitor(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)-- anagentcall whosesubagent_typeisname(a call without one startsgeneral-purpose, so it counts as that, ADR 0020);Skill(name)-- askillcall for the skillname. Exact names.WebFetch(domain:example.com)-- the host ofurl;domain:*.example.commatches 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 calledmcp__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.