Skip to content

Feature: opt-in shared helper modules for rule files, contained within .archgate/ #490

Description

@hancrafted

Problem

Rule files (.rules.ts) must be fully self-contained — the scanner (src/engine/rule-scanner.ts, ALLOWED_MODULES) permits only node:path|url|util|crypto. This is a sound default, and I understand the rationale in the scanner's comment and ASSURANCE-CASE.md: a .rules.ts runs in-process, so a relative import to a file the scanner never sees is an ACE vector.

The cost is that any helper shared across multiple ADRs' rule files (frontmatter parsing, config resolution, glob helpers, kebab-case validation, …) must be copy-pasted into every rule file, along with its tests. Past a handful of ADRs this is real code and test duplication, with no single source of truth and silent drift between copies.

Proposal (security-preserving, opt-in)

Keep the default exactly as-is: relative imports stay blocked. Add an opt-in field in .archgate/config.json by which a project may declare allowed relative import paths — constrained to within .archgate/ itself (e.g. .archgate/lib). This is a hard boundary, not merely a default: any specifier that canonicalizes to a path outside .archgate/ (including .. escapes) is rejected even when configured. Bare specifiers and non-relative paths remain blocked (unchanged — the node_modules/path shadowing concern is unaffected).

Rationale: .archgate/ is already the reviewed governance tree. Containing shared helpers there keeps all governance code — rules and their helpers — in one audited boundary, and the containment rule means the config can never reach arbitrary project code or node_modules. The runtime already supports this: loader.ts imports rule files in place via import(pathToFileURL(...)) on Bun, which transpiles .ts on import — so .archgate-local relative imports resolve today; only the scanner blocks them.

Optional additional hardening (maintainer's choice)

If even governance-local helpers should stay within the capability sandbox, imported .archgate/-local files could be run transitively through the same scanRuleSource allowlist, so a helper still can't reach child_process/fetch/eval. rule-scanner.ts:521–533 suggests imported-file scanning already existed and converged with first-party scanning, so the primitive is largely present.

Scope

The change looks localized to rule-scanner.ts (+ threading config through scanRuleSource, which has a single call site in loader.ts) plus a canonicalize-and-contain path check. Happy to open a PR if the direction is welcome — wanted to align on the security model first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions