Guard Shell

Shell-argument content-safety guard — refuses user-supplied strings that carry shell-injection shapes BEFORE they reach a child-process spawn. The canonical defense is "command + literal argv array, never shell: true" (route through b.processSpawn, which holds that contract); guardShell layers the metacharacter catalog on top so even operator-untrusted strings flowing through the argv slots are screened. KIND=identifier; the gate consumes ctx.identifier (or ctx.arg) and refuses on hostile shapes.

Threat catalog: POSIX shell metacharacters (; & | < > ( ) { } [ ] * ? ~ ! # \ and single/double quotes); backtick command substitution; $(...) command substitution and ${VAR} parameter expansion; process substitution <(...) / >(...); cmd.exe metacharacters (& | < > ^ % " ' ( ) , ; = plus whitespace + newlines); CR / LF / NUL line-splitting; bare $VAR parameter expansion; leading - arguments (-rf / --exec flag-injection class) gated by argHyphenPolicy; BIDI override / zero-width / C0 control / null-byte refuse at every profile.

Profiles: strict / balanced / permissive. Compliance postures: hipaa / pci-dss / gdpr / soc2. Operators select via { profile: "strict" } or { compliancePosture: "hipaa" }; postures overlay on top of the profile baseline.

Shell args cannot be repaired safely — sanitize either passes through clean input or throws GuardShellError; the gate returns serve / audit-only / refuse (no sanitize action). Pair with b.processSpawn so the eventual child_process.spawn call uses shell: false and the screened argv values.

b.guardShell.validate(input, opts) #

stable0.7.13hipaapci-dssgdprsoc2
{
  profile:           "strict"|"balanced"|"permissive",
  compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
  bidiPolicy:        "reject"|"audit"|"allow",
  controlPolicy:     "reject"|"audit"|"allow",
  nullBytePolicy:    "reject"|"audit"|"allow",
  zeroWidthPolicy:   "reject"|"strip"|"audit"|"allow",
  posixMetaPolicy:   "reject"|"audit"|"allow",
  cmdMetaPolicy:     "reject"|"audit"|"allow",
  dollarSubstPolicy: "reject"|"audit"|"allow",
  processSubstPolicy:"reject"|"audit"|"allow",
  backtickPolicy:    "reject"|"audit"|"allow",
  newlinePolicy:     "reject"|"audit"|"allow",
  argHyphenPolicy:   "reject"|"audit"|"allow",
  maxBytes:          number,
  maxRuntimeMs:      number,
}

Inspect a single shell-argument string and return an aggregated issue list. Pure inspection — never throws on hostile input; caller decides what to do with the issues. The ok flag is true only when zero critical / high issues fire. Throws GuardShellError("shell.bad-opt") when a numeric opt is non-finite / negative (config-time mistake by the operator).

var clean = b.guardShell.validate("safe-arg-value", { profile: "strict" });
clean.ok;                                          // → true

var hostile = b.guardShell.validate("safe; rm -rf /", { profile: "strict" });
hostile.ok;                                        // → false
hostile.issues.some(function (i) { return i.kind === "posix-metachar"; });  // → true

b.guardShell.sanitize(input, opts) #

stable0.7.13hipaapci-dssgdprsoc2
{
  profile:           "strict"|"balanced"|"permissive",
  compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
  posixMetaPolicy:   "reject"|"audit"|"allow",
  cmdMetaPolicy:     "reject"|"audit"|"allow",
  dollarSubstPolicy: "reject"|"audit"|"allow",
  processSubstPolicy:"reject"|"audit"|"allow",
  backtickPolicy:    "reject"|"audit"|"allow",
  newlinePolicy:     "reject"|"audit"|"allow",
  argHyphenPolicy:   "reject"|"audit"|"allow",
  maxBytes:          number,
}

Pass-through-or-throw. Shell arguments cannot be safely repaired (stripping a ; inside an arg fundamentally changes operator intent); this primitive returns the input unchanged when no critical or high issue fires, otherwise throws GuardShellError with the offending rule id (e.g. shell.posix-metachar, shell.dollar-substitution, shell.backtick, shell.newline). Operators that need a "best-effort cleanup" semantic should use a different argv shape (path + literal arg array) rather than trying to disarm a hostile string.

var arg = b.guardShell.sanitize("safe-arg-value", { profile: "strict" });
arg;                                               // → "safe-arg-value"

try {
  b.guardShell.sanitize("safe; rm -rf /", { profile: "strict" });
} catch (e) {
  e.code;                                          // → "shell.posix-metachar"
}

b.guardShell.compliancePosture(name) #

stable0.7.13hipaapci-dssgdprsoc2

Look up a compliance-posture overlay by name (one of "hipaa" / "pci-dss" / "gdpr" / "soc2"). Returns a fresh clone of the posture overlay so the caller may mutate it freely without disturbing the shared table. Throws GuardShellError with code "shell.bad-posture" when the name is not one this guard maps. Wired by gateContract.defineGuard through gateContract.lookupCompliancePosture, so the clone semantics and error code are identical across every guard in the family.

var posture = b.guardShell.compliancePosture("hipaa");
posture;                                             // → overlay clone (mutable)

try {
  b.guardShell.compliancePosture("not-a-regime");
} catch (e) {
  e.code;                                            // → "shell.bad-posture"
}

b.guardShell.buildProfile(opts) #

stable0.7.13
{
  extends:   string|string[],   // base profile name(s) to compose
  ...:       any guard key,      // inline override of resolved keys
}

Compose a derived profile from one or more named bases plus inline overrides, resolving names through this guard's own PROFILES table. opts.extends is a base profile name ("strict" / "balanced" / "permissive") or an array of names — later entries shadow earlier ones, and inline opts keys win last. Wired by gateContract.defineGuard through gateContract.makeProfileBuilder, so operator-defined profiles stay traceable to a baseline instead of a hand-typed dictionary.

var custom = b.guardShell.buildProfile({ extends: "strict" });
custom;                                              // → composed profile object

b.guardShell.loadRulePack(pack) #

stable0.7.13

Register an operator-supplied rule pack with this guard's rule-pack registry. The pack is identified by pack.id (a non-empty string) and stored for later dispatch by gates that opt in via opts.rulePackId. Returns the pack unchanged on success; throws GuardShellError with code "shell.bad-opt" when pack is missing or pack.id is not a non-empty string. Wired by gateContract.defineGuard through gateContract.makeRulePackLoader, so storage shape and validation are identical across the family.

var pack = b.guardShell.loadRulePack({ id: "tenant-policy", rules: [] });
pack.id;                                             // → "tenant-policy"

b.guardShell.gate(opts?) #

stable0.7.13
{
  profile:           string,    // one of PROFILES; default this guard's default
  compliancePosture: string,    // overlay one of hipaa/pci-dss/gdpr/soc2
  mode:              string,    // one of gateContract MODES; default "enforce"
}

Build the guard's request-boundary gate — a contract-shaped object exposing check(ctx) that host primitives call at their byte moment. This is the factory default chain: serve when no issue, audit-only for info / warn issues, and refuse for any high / critical issue, dispatched to the right ctx field by the guard's KIND. Wired by gateContract.defineGuard through gateContract.buildGuardGate; a guard whose gate diverges (a bespoke sanitize-and-reserialize chain, for example) ships its own gate block instead of this template.

var gate = b.guardShell.gate({ profile: "strict" });
var decision = await gate.check({ bytes: Buffer.from("...") });
decision.action;                                     // → "serve" | "refuse" | …

b.guardShell.resolveOpts(opts?) #

stable0.7.13
{
  profile:           string,    // one of PROFILES; default this guard's default
  compliancePosture: string,    // overlay one of hipaa/pci-dss/gdpr/soc2
}

Resolve caller opts against this guard's PROFILES + compliance-posture overlays into the fully-defaulted option set the guard runs on — the same resolution validate / sanitize / gate apply internally. Wired by gateContract.defineGuard from the guard's binding config (profiles / postures / defaults / error class), so a guard's bespoke gate calls resolveOpts instead of re-declaring the per-guard resolver wrapper. Throws GuardShellError with code "shell.bad-opt" / "shell.bad-posture" on an unknown profile or posture name.

var resolved = b.guardShell.resolveOpts({ profile: "strict" });
resolved.profile;                                    // → "strict"

Last updated 2026-08-08T16:39:49.652Z by seeder.