owthorize - npm
npm
Search<br>Sign UpSign In
owthorize
0.4.2 • Public • Published 2 months ago<br>Readme<br>Code Beta<br>3 Dependencies<br>0 Dependents<br>2 Versions<br>owthorize
Prompt safeguards are theater. The model isn't the boundary — the layer between it and your systems is.
owthorize is a synchronous JS/TS gate that catches destructive AI-agent tool calls before they execute , using ASTs and parsed shapes — not regex on strings. It sits at the tool layer of your agent (OpenAI, Anthropic, LangChain, Vercel AI SDK) and gates every call through a small rule engine.
// the model emits this:<br>db.query("DROP TABLE users")<br>// │<br>// ▼ guard.tool() intercepts, parses, denies<br>// │<br>throw new GuardDenied("DDL not allowed: drop")
The problem
Modern AI agents call tools. Tools have side effects. Three things go wrong:
Prompt injection. A user (or a document the agent reads) coerces the model into emitting calls the developer never anticipated.
Hallucinated arguments. The model forms a syntactically-valid call with semantically-wrong inputs — DELETE FROM users instead of DELETE FROM users WHERE id = ?.
Reasoning errors. The model tries to "be helpful" by issuing destructive cleanup, fetches an internal IP it shouldn't reach, or writes outside the workspace.
Prompt-level safeguards ("you are a helpful assistant, do not drop tables") fail under all three. The right boundary is the layer between the model and your database/HTTP/disk — the tooling layer. That's where owthorize lives.
What it catches
SQL DDL — DROP, TRUNCATE, ALTER, CREATE, RENAME (Postgres / MySQL / SQLite, AST-level)
Unbounded mutations — UPDATE / DELETE with no WHERE clause
SSRF targets — RFC1918, link-local, loopback, AWS metadata 169.254.169.254, IPv4-mapped IPv6 ([::ffff:127.0.0.1]), *.internal wildcards
Shell metacharacters and dangerous commands — basename match (/usr/bin/rm → rm), pipe / redirect / $() / backtick detection
Path traversal — resolved-path containment, prefix-collision-safe (root /safe doesn't match /safe-evil)
Project-specific policy — typed custom rules per adapter (rules.sql.custom, rules.http.custom, etc.)
Who it's for
JS/TS developers shipping AI agents that touch:
a database (Postgres, MySQL, SQLite — via mysql2, pg, sqlite3, etc.)
outbound HTTP (fetch, axios, undici)
the filesystem (fs/promises)
shell exec (child_process)
If your agent is read-only against trusted data, you probably don't need this. If it can write — or if it can fetch arbitrary URLs — owthorize closes the most-likely failure modes before they ship.
Install
npm install owthorize
Both ESM and CJS are supported. Node ≥ 18.
Quickstart
import { Guard, rules, GuardDenied } from "owthorize"
const guard = new Guard({<br>rules: [<br>rules.sql.denyDDL(),<br>rules.sql.denyMutationWithoutWhere(),<br>rules.http.denyHosts(rules.http.SSRF_DEFAULTS),<br>],<br>})
const safeQuery = guard.tool("db.query", {<br>adapter: "sql.postgres",<br>handler: async ({ query }: { query: string }) => db.query(query),<br>})
try {<br>await safeQuery({ query: "DROP TABLE users" })<br>} catch (err) {<br>if (err instanceof GuardDenied) {<br>console.log(err.matched, "→", err.reason)<br>// → sql.denyDDL → DDL not allowed: drop
await safeQuery({ query: "SELECT id FROM users WHERE id = $1" })<br>// runs the handler
Test rules without side effects:
guard.simulate("db.query", { query: "DROP TABLE users" })<br>// → { decision: "deny", reason: "DDL not allowed: drop", matched: "sql.denyDDL", irreversible: true }
For the full guide (custom rules, framework shims, audit log, failure modes, the irreversible flag): see USAGE.md.
What's in the box
Adapters
Adapters parse the raw payload into a typed shape. Rules see ASTs, not strings.
Adapter<br>Payload<br>Parses into
sql.postgres / sql.mysql / sql.sqlite
{ query, params? }
kind, tables, hasWhere, ddlOp, dialect
http<br>{ url, method?, headers?, body? }<br>parsed URL with IPv4-mapped IPv6 normalization, lowercased header keys
shell
{ command } or { argv }
tokenized argv, metacharacter / pipe / redirect / substitution flags
fs<br>{ path, op? }<br>normalized absolute path, op classification
raw<br>anything<br>passthrough — for cross-adapter custom rules
Built-in rules
Rule<br>Blocks<br>Tags irreversible
rules.sql.denyDDL()
DROP, TRUNCATE, ALTER, CREATE, RENAME
yes
rules.sql.denyMutationWithoutWhere()
UPDATE / DELETE with no WHERE
yes
rules.sql.denyTables({ deny, allow })<br>Configured table denylist or allowlist (case-insensitive, schema-stripped)<br>yes for writes, no for SELECT
rules.http.denyHosts(list)<br>Host literals, IPv4/IPv6 CIDRs, *.wildcards
no
rules.http.allowHosts(list)<br>Anything not on the list<br>no
rules.http.SSRF_DEFAULTS<br>RFC1918, link-local, loopback, AWS metadata, *.internal, *.local
(constant)
rules.shell.denyCommands(list, opts?)<br>Banned commands (basename) plus metacharacter abuse<br>yes for rm/dd/mkfs/etc., configurable via destructive option
rules.fs.confineTo(roots, opts?)<br>Anything outside the configured roots<br>yes for write/delete, no for read/list
rules..custom({ on, when,...