Skip to content
Job sheet 001 · Worked example Verified

Run an approved command

Build a one-program catalogue for your own tool, feed it stdin, read the result, and meet both ways Cuprum refuses a program.

Verified 2026-10-19 · Cuprum 0.2.0b2 · Python 3.12 · Linux

BeginnerCataloguePolicy

01 — Problem

The job

A script needs one external tool — here, wc, to count the words in a piece of text. With subprocess that is a string or a list assembled wherever it is needed, run with whatever is on the machine. With Cuprum the program is declared once in a catalogue, the command is built from typed arguments, and the script says exactly which programs it may run.

This job builds that catalogue, feeds the text on standard input rather than as an argument, and turns the captured output into an integer.

02 — Before you start

Before you start

Level
Beginner
Needs
Python 3.12 or later, and wc on PATH (any POSIX system)
Install
Cuprum 0.2.0b2 from PyPI; see Getting started
Tested
2026-10-19 against Cuprum 0.2.0b2 on Linux, Python 3.12
Uses
ProgramCatalogue.from_programs, sh.make, StdinInput, scoped(catalogue=…), DEFAULT_CATALOGUE

03 — Code

The code

word_count.py Complete
from cuprum import (
    Program,
    ProgramCatalogue,
    StdinInput,
    scoped,
    sh,
)

WC = Program("wc")
CATALOGUE = ProgramCatalogue.from_programs(WC, name="word-count")

count_words = sh.make(WC, catalogue=CATALOGUE)


def word_count(text: str) -> int:
    cmd = count_words("-w")
    with scoped(catalogue=CATALOGUE):
        result = cmd.run_sync(stdin=StdinInput(text=text))
    if not result.ok:
        msg = f"wc failed with exit code {result.exit_code}: {result.stderr}"
        raise RuntimeError(msg)
    return int((result.stdout or "").strip())


print(count_words("-w").argv_with_program)
print(count_words("-w").project.name)
print(word_count("Every command gets the right fitting."))
Verified 2026-10-19 · Cuprum 0.2.0b2 · Python 3.12 · Linux

Reading it

  • ProgramCatalogue.from_programs(WC, name="word-count") builds a single-project catalogue whose allowlist is exactly wc. Without name=, the project would be named after the programs' base names.
  • sh.make(WC, catalogue=CATALOGUE) returns a builder. It has already checked that wc is catalogued, so every command it builds carries the project's metadata.
  • StdinInput(text=...) is encoded with the context's encoding (UTF-8 by default) and written to the child's standard input, so the text never appears in argv.
  • strip() is there because wc pads its count differently on different systems. Capture keeps the child's bytes as they were.

04 — Output

Expected output

Expected outputstdout
('wc', '-w')
word-count
6

The first line is the exact argv Cuprum will pass — program first, no shell. The second is the project the command belongs to, which is what downstream tooling reads to find noise rules and documentation links. The third is the count.

05 — Failure path

When it fails

Cuprum refuses a program at two different moments, for two different reasons. This script triggers both.

refusals.py Failure path
from cuprum import (
    DEFAULT_CATALOGUE,
    ECHO,
    ForbiddenProgramError,
    Program,
    ProgramCatalogue,
    UnknownProgramError,
    scoped,
    sh,
)

WC = Program("wc")
CATALOGUE = ProgramCatalogue.from_programs(WC, name="word-count")

# Not in the catalogue: refused when the builder is made.
try:
    sh.make(Program("curl"), catalogue=CATALOGUE)
except UnknownProgramError as exc:
    print(f"UnknownProgramError: {exc}")

# In a catalogue, but outside the active scope: refused when it runs.
with scoped(catalogue=CATALOGUE):
    try:
        sh.make(ECHO, catalogue=DEFAULT_CATALOGUE)("hello").run_sync()
    except ForbiddenProgramError as exc:
        print(f"ForbiddenProgramError: {exc}")
Verified 2026-10-19 · Cuprum 0.2.0b2 · Python 3.12 · Linux
Expected outputstdout
UnknownProgramError: Program 'curl' is not in the catalogue allowlist
ForbiddenProgramError: Program 'echo' is not allowed in the current context
Also expectedstderr, logger cuprum.context
Program echo denied by context allowlist restricted_state=True

UnknownProgramError is a construction failure: the catalogue you passed does not know the program, so no command exists to run. ForbiddenProgramError is an authorization failure: ECHO is in Cuprum's default catalogue, so the command was built, but the scope around it allows only wc. Nothing is spawned in either case.

The second refusal names its catalogue on purpose. Inside scoped(catalogue=...), sh.make resolves a program from the scope's catalogue, so a bare sh.make(ECHO) there would be the first refusal again: echo is not in the word-count catalogue, and the builder is never made. Passing catalogue=DEFAULT_CATALOGUE builds the command from the catalogue that does list echo, which is what lets the scope's allowlist be the check that fails.

A non-zero exit is not an exception. If wc itself failed, result.ok would be false and the script above raises its own RuntimeError with the exit code and stderr.

06 — Why it is allowed

Why the policy permits it

Two independent checks let this run. The builder was made from a catalogue that lists wc, and the scoped() block takes its allowlist from that same catalogue, so wc is the one program the block may run. The block also makes that catalogue the one sh.make consults, so a builder made inside it could drop the catalogue= argument; this script names it anyway, because the builder is made before the block.

The scope is what a reviewer reads. Remove the with block and the command still runs, because an unscoped root context permits every catalogued program; that default exists to ease adoption, and narrowing it is how a script states its policy.