harn-stdlib 0.8.28

Embedded Harn standard library source catalog
Documentation
/**
 * std/net_policy — per-harness `harness.net.*` access policy.
 *
 * Issue: harn#1913 (E4.9), epic #1765 (Harness explicit-capability).
 *
 * `harness.net.*` is the ambient-free entry point for network I/O in
 * Harn scripts. By default it is gated only by the process-wide
 * `egress_policy(...)` allowlist (when configured). A `NetPolicy`
 * value layered on top of an individual harness lets the script
 * declare an allowlist / denylist / default action and pick what
 * happens on a violation (throw, audit-only allow, quarantine the
 * agent, or run a custom callback). The dispatcher in
 * `crates/harn-vm/src/vm/methods/harness.rs` runs the policy ahead of
 * the underlying `net.get` / `net.http_get` call so audit_only and
 * quarantine outcomes apply uniformly to real and mock paths.
 *
 *   import { NetPolicy } from "std/net_policy"
 *
 *   pipeline default() {
 *     pipeline_on_finish({ harness, return_value ->
 *       let policy = NetPolicy.create({
 *         allow: [
 *           NetPolicy.domain("github.com"),
 *           NetPolicy.domain_wildcard("*.github.com"),
 *           NetPolicy.cidr("10.0.0.0/8"),
 *           NetPolicy.host("api.anthropic.com", [443]),
 *         ],
 *         deny: [NetPolicy.domain_wildcard("*.competitor.com")],
 *         default: "deny",
 *         on_violation: "error",
 *       })
 *       let restricted = harness.with_net_policy(policy)
 *       restricted.net.get("https://github.com/foo")    // allowed
 *       restricted.net.get("https://example.com/bar")   // throws NetPolicyViolation
 *       return return_value
 *     })
 *     return 0
 *   }
 *
 * `on_violation` accepts a string (`"error"`, `"audit_only"`,
 * `"quarantine"`) or a closure shaped `fn(req) -> string` that picks
 * one of the three outcomes per-request. Quarantine deny events flip
 * the harness's `is_quarantined()` flag so downstream tooling can
 * route on the signal.
 *
 * `HARN_NET_POLICY_BYPASS=1` short-circuits enforcement on every
 * harness in the process. The bypass is recorded on the audit log
 * with `bypass: true` so the trust graph still sees the leak.
 */
/**
 * Allow exactly one host (case-insensitive, IDNA-normalised). Does
 * not match subdomains; use `domain_wildcard` for that.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: NetPolicy.domain("github.com")
 */
pub fn domain(host) {
  return __net_policy_domain(host)
}

/**
 * Allow any subdomain of `suffix`. Pattern must start with `*.`. The
 * bare apex is NOT matched (`*.github.com` does not match `github.com`
 * — list the apex separately if you need it).
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: NetPolicy.domain_wildcard("*.github.com")
 */
pub fn domain_wildcard(pattern) {
  return __net_policy_domain_wildcard(pattern)
}

/**
 * Allow any host whose resolved IP falls inside the supplied CIDR
 * range. URLs whose host is a literal IP are matched directly. For
 * hostnames the dispatcher relies on the URL parser's host string;
 * full DNS-resolved IP matching is layered on by the future
 * `harness.net.*` real-mode wiring (E4.4 / harn#1769).
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: NetPolicy.cidr("10.0.0.0/8")
 */
pub fn cidr(range) {
  return __net_policy_cidr(range)
}

/**
 * Allow one host on a specific list of ports. Pass `nil` (or omit)
 * for any port. `ports` must be a list of u16 integers.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: NetPolicy.host("api.anthropic.com", [443])
 */
pub fn host(host_name, ports) {
  return __net_policy_host(host_name, ports)
}

/**
 * Compile a policy from `{allow, deny, default, on_violation}`.
 * Defaults are `default: "deny"` and `on_violation: "error"`. `allow`
 * / `deny` accept tagged-rule values from this module or bare strings
 * (interpreted as domain, wildcard, or CIDR by shape).
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: NetPolicy.create({allow: [NetPolicy.domain("github.com")], default: "deny"})
 */
pub fn create(config) {
  return __net_policy_create(config)
}

/**
 * `OnViolation()` returns the named-action namespace as a dict so
 * authors can use dotted access (e.g. `OnViolation.audit_only`). The
 * strings are the same shape `NetPolicy.create({..., on_violation:
 * "..."})` accepts, so the constants are interchangeable with the raw
 * strings.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: NetPolicy.create({on_violation: OnViolation.audit_only})
 */
pub fn OnViolation() {
  return {error: "error", audit_only: "audit_only", quarantine: "quarantine"}
}

/**
 * `NetPolicy()` returns the namespace dict so callers can use dotted
 * access (e.g. `NetPolicy.create({...})`) after a single import. This
 * mirrors the `OnBudget()` / `QueueStrategy()` factories elsewhere in
 * the stdlib.
 *
 * @effects: []
 * @allocation: heap
 * @errors: []
 * @api_stability: experimental
 * @example: let NetPolicy = NetPolicy()
 */
pub fn NetPolicy() {
  return {
    domain: domain,
    domain_wildcard: domain_wildcard,
    cidr: cidr,
    host: host,
    create: create,
    OnViolation: OnViolation(),
  }
}