oc_chat
Loading...
Searching...
No Matches
oc_chat.tools._argv_policy Namespace Reference

Classes

class  ArgvDecision

Functions

ArgvDecision _deny (list[str] argv, str program, str reason)
str _metachar_denial (frozenset[str] chars)
tuple[frozenset[str], frozenset[str], str] _scan_metachars (str raw)
bool _is_plain_name (str token)
list[str] _normalize_options (list[str] tokens, frozenset[str] value_opts)
ArgvDecision classify_command (str command, Path sandbox_root)
bool _short_cluster_names_index (str token)
str pip_index_option (list[str] tokens)
str pip_index_denial (str token)
ArgvDecision _classify_pip (list[str] argv, list[str] rest)
ArgvDecision _classify_where (list[str] argv, str program, list[str] rest)
ArgvDecision _classify_listing (list[str] argv, str program, list[str] rest, Path sandbox_root)

Variables

frozenset _SHELL_METACHARS = frozenset("|&;<>$`%\n\r\0")
frozenset _TIER2_PROGRAMS = frozenset({"python", "python3", "py", "pytest", "oct"})
frozenset WINDOWS_BUILTINS = frozenset({"dir", "echo"})
frozenset PIP_INDEX_REACHING_SUBCOMMANDS = frozenset({"install", "download"})
tuple _PIP_INDEX_LONG_OPTIONS
frozenset _PIP_INDEX_SHORT_OPTIONS = frozenset({"i", "f"})
frozenset _PIP_SHORT_VALUE_OPTIONS = frozenset({"r", "c", "e", "t", "b", "d"})
frozenset _PIP_SHORT_FLAG_OPTIONS = frozenset({"v", "q", "U", "I", "h"})
tuple _PIP_INDEX_DENIAL_CLAUSE
frozenset _QUOTED_METACHAR_PROGRAMS = _TIER2_PROGRAMS

Detailed Description

Whole-argv command policy (M8.0 WU-3; Appendix A / LD-4).

Purpose
-------
Replaces the legacy longest-prefix ``startswith`` whitelist + ``shell=True``
execution with a deterministic, whole-argv allowlist evaluated against the
locked Appendix A table. The model's command string is:

1. scanned for shell metacharacters (``| & ; < > $`` / backtick / ``%`` /
   newline / NUL) with POSIX quote tracking. Any metacharacter OUTSIDE quotes is
   rejected outright, for every program -- there is no ``shell=True`` lane, so
   these can only be injection (``git status && x``, ``echo $(cat .env)``, pipes,
   redirects). A metacharacter found ONLY inside a quoted argument is admitted
   for the directly-launched Tier-2 programs alone
   (:data:`_QUOTED_METACHAR_PROGRAMS`), where nothing re-parses the string and it
   reaches the child as data (SHELL-01; operator ruling 2026-09-04, a narrow
   named amendment to the M8.1 lock's LD-4 wording). An unterminated quote is a
   deny, never a fallthrough, and NUL is denied wherever it appears;
2. parsed to argv via :func:`shlex.split`;
3. dispatched on the (basename, lowercased) program and validated as a WHOLE
   argv -- subcommands, options, and path args each checked; an unknown option
   at any position, an out-of-sandbox path arg, or an unknown program ⇒ deny.

Responsibilities
----------------
- :func:`classify_command` -- the single entry point: metacharacter gate, parse,
  program normalization, then dispatch to a per-program validator.
- :func:`_scan_metachars` -- the quote-aware metacharacter scan (SHELL-01).
- :func:`_metachar_denial` -- the denial that names the character, the reason
  and the sanctioned routes.
- :func:`_classify_pip` / :func:`_classify_where` / :func:`_classify_listing` --
  the per-program whole-argv validators.
- :func:`_normalize_options` -- canonical option normalization ("normalize then
  match"), run before any option is compared.
- :func:`pip_index_option` / :func:`pip_index_denial` -- the package-index and
  trusted-host option family, and the denial it earns on ``pip install`` /
  ``pip download``. Shared with the execution seam in :mod:`oc_chat.tools.shell`,
  which pins the index the operator trusts and refuses the ``python -m pip``
  spelling of the same command on the same terms (M8 F78, SUPPLY-02).
- :class:`ArgvDecision` -- the frozen decision record the caller acts on.

Tier-1 (allow, ``shell=False``): ``pip list``/``show``/``freeze``,
``where``/``which`` (one bare program name), ``dir``/``ls`` (``-l``/``-a``/
``--color=never`` + ≤1 in-sandbox path), ``echo`` (argv-only). Tier-2 (confirm/
grant): ``python``/``pytest``/``pip install``/``pip uninstall``/``oct``. Reads
go through the filesystem tools and ``git`` through ``oct_git_*`` -- so ``git``
(freeform), ``cat``/``type``/``grep``/``findstr``, ``curl``/``wget``,
``bash``/``sh``/``powershell``/``pwsh``/``node`` and the cmd.exe mutators
(``del``/``copy``/``move``/``ren``/``md``/``rmdir``) are **denied**.

The ``_get_cross_platform_command`` Unix→PowerShell conversion is dropped (F55):
no converted form is admitted.

Diagnostics
-----------
Domain: OC_CHAT.TOOLS
Levels:
    L2 -- lifecycle events
    L3 -- semantic details (the decision and the reason it carries)
    L4 -- deep tracing

Contracts
---------
- Classification is PURE: no writes, no subprocess, no network. The only
  external read is :func:`require_inside_sandbox` resolving a path argument.
- Deny is the default. An unknown program, an unknown option at any position,
  or an out-of-sandbox path argument denies rather than falling through.
- A pre-parse deny carries an empty ``argv`` -- nothing was parsed yet.

Dependencies
------------
- oc_diagnostics (structured debug logging via _dbg)
- oc_chat.tools._sandbox (require_inside_sandbox, SandboxViolation - the sandbox check on a dir or ls path argument)

Function Documentation

◆ _classify_listing()

ArgvDecision oc_chat.tools._argv_policy._classify_listing ( list[str] argv,
str program,
list[str] rest,
Path sandbox_root )
protected

Definition at line 428 of file _argv_policy.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _classify_pip()

ArgvDecision oc_chat.tools._argv_policy._classify_pip ( list[str] argv,
list[str] rest )
protected

Definition at line 392 of file _argv_policy.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _classify_where()

ArgvDecision oc_chat.tools._argv_policy._classify_where ( list[str] argv,
str program,
list[str] rest )
protected

Definition at line 422 of file _argv_policy.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _deny()

ArgvDecision oc_chat.tools._argv_policy._deny ( list[str] argv,
str program,
str reason )
protected

Definition at line 173 of file _argv_policy.py.

Here is the caller graph for this function:

◆ _is_plain_name()

bool oc_chat.tools._argv_policy._is_plain_name ( str token)
protected
True if *token* is a bare identifier (no path separators / traversal),
suitable as a ``where``/``which`` target or a ``pip show`` package name.

Definition at line 257 of file _argv_policy.py.

Here is the caller graph for this function:

◆ _metachar_denial()

str oc_chat.tools._argv_policy._metachar_denial ( frozenset[str] chars)
protected
Build the metacharacter denial, which must TEACH THE ROUTE (SHELL-01, P3).

In order: the offending character(s); one clause saying why (nothing on this
path interprets them); then the sanctioned routes, including the fact that a
directly-launched Tier-2 program does accept them inside a quoted argument.
The raw command is deliberately NOT echoed back -- a denied string may carry
text an attacker wrote for the model to read.

Definition at line 177 of file _argv_policy.py.

Here is the caller graph for this function:

◆ _normalize_options()

list[str] oc_chat.tools._argv_policy._normalize_options ( list[str] tokens,
frozenset[str] value_opts )
protected
Canonically normalize option tokens before matching (Appendix A,
"normalize then match"): expand bundled short options (``-la`` -> ``-l -a``)
and unify space-separated long-option values (``--opt val`` -> ``--opt=val``
for opts in *value_opts*). Leaves positional (path/name) args untouched.

Definition at line 265 of file _argv_policy.py.

Here is the caller graph for this function:

◆ _scan_metachars()

tuple[frozenset[str], frozenset[str], str] oc_chat.tools._argv_policy._scan_metachars ( str raw)
protected
Split the shell metacharacters in *raw* by quote state (SHELL-01, P1).

Walks the raw string tracking quote state under the SAME rules
:func:`shlex.split` applies with ``posix=True``, because a divergence between
this scanner and the parser that produces the argv is itself the bug class
this guards: single quotes make everything literal until the closing quote;
inside double quotes only ``\\"`` and ``\\\\`` are escapes; outside quotes a
backslash escapes the next character.

Returns ``(outside, inside, error)`` — the metacharacters found outside any
quote, those found inside one, and a non-empty *error* when the string cannot
be parsed at all (an unterminated quote, a trailing escape). An unparseable
string is a DENY at the call site, never a fallthrough: a scanner that cannot
say what is quoted must not admit anything. A backslash-escaped metacharacter
outside quotes counts as OUTSIDE — fail-closed; only genuine quoting admits.

Definition at line 199 of file _argv_policy.py.

Here is the caller graph for this function:

◆ _short_cluster_names_index()

bool oc_chat.tools._argv_policy._short_cluster_names_index ( str token)
protected
True when a single-dash pip *token* spells ``-i`` or ``-f`` (253 P2).

Walks the option letters the way optparse does: a flag letter continues the
cluster, a value-taking letter ends it (everything after it is that option's
value, not an option letter), and an unknown letter ends it too — pip itself
rejects the command in that case, so there is nothing left to guard.

Definition at line 342 of file _argv_policy.py.

Here is the caller graph for this function:

◆ classify_command()

ArgvDecision oc_chat.tools._argv_policy.classify_command ( str command,
Path sandbox_root )
Classify *command* against the Appendix A whole-argv allowlist.

Definition at line 288 of file _argv_policy.py.

Here is the call graph for this function:

◆ pip_index_denial()

str oc_chat.tools._argv_policy.pip_index_denial ( str token)
The denial for a model-named package index — it must TEACH THE ROUTE.

Naming the offending token matters: the model has to know which option to
drop, and "your command was denied" without it invites the same command back
in a different spelling. The route is the operator's, out of band, so the
text says so rather than implying a retry could succeed.

Definition at line 378 of file _argv_policy.py.

Here is the caller graph for this function:

◆ pip_index_option()

str oc_chat.tools._argv_policy.pip_index_option ( list[str] tokens)
The first token in *tokens* naming an index or a trusted host, or ``""``.

Recognises every spelling the family has (253 P2): the long options with a
space- or ``=``-separated value, an unambiguous long-option ABBREVIATION,
``--no-index``, and the short ``-i`` / ``-f`` alone, bundled behind flags, or
with the value attached. ``--`` and a bare ``-`` are not options.

Definition at line 359 of file _argv_policy.py.

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ _PIP_INDEX_DENIAL_CLAUSE

tuple oc_chat.tools._argv_policy._PIP_INDEX_DENIAL_CLAUSE
protected
Initial value:
1= (
2 "the index is pinned to the operator-trusted value; remove the option"
3)

Definition at line 143 of file _argv_policy.py.

◆ _PIP_INDEX_LONG_OPTIONS

tuple oc_chat.tools._argv_policy._PIP_INDEX_LONG_OPTIONS
protected
Initial value:
1= (
2 "--index-url", "--extra-index-url", "--find-links", "--trusted-host", "--no-index",
3)

Definition at line 123 of file _argv_policy.py.

◆ _PIP_INDEX_SHORT_OPTIONS

frozenset oc_chat.tools._argv_policy._PIP_INDEX_SHORT_OPTIONS = frozenset({"i", "f"})
protected

Definition at line 129 of file _argv_policy.py.

◆ _PIP_SHORT_FLAG_OPTIONS

frozenset oc_chat.tools._argv_policy._PIP_SHORT_FLAG_OPTIONS = frozenset({"v", "q", "U", "I", "h"})
protected

Definition at line 138 of file _argv_policy.py.

◆ _PIP_SHORT_VALUE_OPTIONS

frozenset oc_chat.tools._argv_policy._PIP_SHORT_VALUE_OPTIONS = frozenset({"r", "c", "e", "t", "b", "d"})
protected

Definition at line 134 of file _argv_policy.py.

◆ _QUOTED_METACHAR_PROGRAMS

frozenset oc_chat.tools._argv_policy._QUOTED_METACHAR_PROGRAMS = _TIER2_PROGRAMS
protected

Definition at line 161 of file _argv_policy.py.

◆ _SHELL_METACHARS

frozenset oc_chat.tools._argv_policy._SHELL_METACHARS = frozenset("|&;<>$`%\n\r\0")
protected

Definition at line 102 of file _argv_policy.py.

◆ _TIER2_PROGRAMS

frozenset oc_chat.tools._argv_policy._TIER2_PROGRAMS = frozenset({"python", "python3", "py", "pytest", "oct"})
protected

Definition at line 106 of file _argv_policy.py.

◆ PIP_INDEX_REACHING_SUBCOMMANDS

frozenset oc_chat.tools._argv_policy.PIP_INDEX_REACHING_SUBCOMMANDS = frozenset({"install", "download"})

Definition at line 115 of file _argv_policy.py.

◆ WINDOWS_BUILTINS

frozenset oc_chat.tools._argv_policy.WINDOWS_BUILTINS = frozenset({"dir", "echo"})

Definition at line 110 of file _argv_policy.py.