@agent-native/core 0.28.2 → 0.28.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -93,6 +93,7 @@ You do NOT get auto-injected screen state. **Call `pnpm action view-screen` at t
93
93
  - Use `db-exec UPDATE` only when no domain action exists and you need a small ad-hoc change.
94
94
  - Use `db-patch` when you only need to tweak a small slice of a **large** text/JSON column (documents, slide HTML, dashboard/form JSON). It saves tokens by sending `{find, replace}` instead of re-transmitting the whole column. Targets exactly one row per call — narrow `--where` by primary key. Supports `--edits '[{find,replace},...]'` for batch edits and `--all` for replace-every-occurrence.
95
95
  - If a template-specific action exists (e.g. `edit-document`, `update-slide`), prefer it — those also push live updates to any open collaborative editor.
96
+ - **Database admin (dev only):** in development, `db-admin-query` / `db-admin-mutate` / `db-admin-rows` / `db-admin-tables` / `db-admin-schema` give **unscoped, full-database** access to ANY table — including framework tables and tables without `owner_email`/`org_id`. Prefer these over `db-exec`/`db-query` for database-admin work and for any non-owner-scoped table: `db-exec`/`db-query` auto-scope to the current user and return **0 rows** on unscoped tables. These mirror the in-app Database admin UI, so prompts and the UI do the same thing.
96
97
 
97
98
  ## Skills
98
99
 
@@ -11,15 +11,16 @@ Agent-native apps are designed to be secure by default. The framework provides a
11
11
 
12
12
  The framework architecture prevents common vulnerabilities when you use the standard patterns:
13
13
 
14
- | Vulnerability | Framework Protection |
15
- | --------------- | --------------------------------------------------------------- |
16
- | SQL injection | Parameterized queries in `db-query`/`db-exec` and Drizzle ORM |
17
- | XSS | React auto-escapes JSX; TipTap sanitizes rich text |
18
- | Data leaks | SQL-level scoping via temporary views (`owner_email`, `org_id`) |
19
- | Auth bypass | Auth guard auto-protects all `defineAction` endpoints |
20
- | Input injection | Zod schema validation in `defineAction` |
21
- | CSRF | `SameSite=lax` + `httpOnly` cookies |
22
- | Secret exposure | `.env` files gitignored; OAuth tokens in dedicated store |
14
+ | Vulnerability | Framework Protection |
15
+ | --------------- | ---------------------------------------------------------------------- |
16
+ | SQL injection | Parameterized queries in `db-query`/`db-exec` and Drizzle ORM |
17
+ | XSS | React auto-escapes JSX; TipTap sanitizes rich text |
18
+ | Data leaks | SQL-level scoping via temporary views (`owner_email`, `org_id`) |
19
+ | Auth bypass | Auth guard auto-protects all `defineAction` endpoints |
20
+ | Input injection | Zod schema validation in `defineAction` |
21
+ | CSRF | `SameSite=lax` + `httpOnly` cookies |
22
+ | Secret exposure | `.env` gitignored; credentials & vault encrypted at rest (AES-256-GCM) |
23
+ | SSRF | `ssrfSafeFetch` blocks internal/metadata targets + redirect rebinding |
23
24
 
24
25
  ## Input Validation {#input-validation}
25
26
 
@@ -67,6 +68,18 @@ React auto-escapes all JSX expressions. Additional guidelines:
67
68
  - For rich text editing, use TipTap (framework dependency) — it sanitizes through its schema
68
69
  - For rendering markdown, use `react-markdown` — it converts to React elements safely
69
70
 
71
+ ## Server-Side Fetch (SSRF) {#ssrf}
72
+
73
+ Any server-side `fetch` of a user- or agent-controlled URL must go through the framework SSRF guard, or it can be pointed at cloud metadata (`169.254.169.254`), `localhost`, or internal services:
74
+
75
+ ```typescript
76
+ import { ssrfSafeFetch } from "@agent-native/core/extensions/url-safety";
77
+
78
+ const res = await ssrfSafeFetch(userProvidedUrl, {}, { maxRedirects: 3 });
79
+ ```
80
+
81
+ `ssrfSafeFetch` blocks private/internal targets, re-checks the resolved IP at connect time (DNS rebinding), and re-validates every redirect hop so a public URL can't redirect into the private network. The extension iframe proxy, `upload-image`, and the design-token importer all route through it. For a pre-flight-only check, use `isBlockedExtensionUrlWithDns(url)` with `redirect: "manual"`.
82
+
70
83
  ## Data Scoping {#data-scoping}
71
84
 
72
85
  In production, the framework automatically restricts agent SQL queries to the current user's data. This is enforced at the SQL level — agents cannot bypass it. This section is the canonical reference for the scoping pipeline; the [Authentication](/docs/authentication) and [Multi-Tenancy](/docs/multi-tenancy) docs link here for the mechanics.
@@ -106,6 +119,8 @@ CREATE TEMPORARY VIEW "notes" AS
106
119
 
107
120
  INSERT statements get `owner_email` auto-injected when the column isn't already present.
108
121
 
122
+ The `db-query` / `db-exec` tools reject schema-qualified table references (`public.<table>`, `main.<table>`) — a qualified name resolves to the base table and would bypass the temporary view above. Agents use bare table names; scoping is applied automatically.
123
+
109
124
  ### Per-Org Scoping (`org_id`)
110
125
 
111
126
  For multi-user apps where teams share data, add an `org_id` column. When both columns are present, queries are scoped by both: `WHERE owner_email = ? AND org_id = ?`.
@@ -141,13 +156,17 @@ pnpm action db-check-scoping --require-org # Also require org_id
141
156
 
142
157
  ## Secrets Management {#secrets}
143
158
 
144
- | Secret type | Where to store |
145
- | ------------------------------- | -------------------------------------------- |
146
- | API keys (OpenAI, Stripe, etc.) | `.env` file (gitignored, server-side only) |
147
- | OAuth tokens (Google, GitHub) | `oauth_tokens` store via `saveOAuthTokens()` |
148
- | Session tokens | Automatic (Better Auth handles this) |
159
+ | Secret type | Where to store |
160
+ | ---------------------------------- | ---------------------------------------------------------- |
161
+ | Deploy-level keys (one per app) | `.env` file (gitignored, server-side only) |
162
+ | Per-user / per-org API keys | `saveCredential` / `resolveCredential` (encrypted at rest) |
163
+ | Registered secrets (sidebar vault) | `app_secrets` vault (encrypted at rest) |
164
+ | OAuth tokens (Google, GitHub) | `oauth_tokens` store via `saveOAuthTokens()` |
165
+ | Session tokens | Automatic (Better Auth handles this) |
166
+
167
+ Per-user/per-org credentials and the vault are encrypted at rest with AES-256-GCM, keyed by `SECRETS_ENCRYPTION_KEY` (falling back to `BETTER_AUTH_SECRET`); production refuses to start without one. To encrypt any pre-existing plaintext credential rows in place, run `pnpm action db-migrate-encrypt-credentials` (idempotent, non-destructive).
149
168
 
150
- Never store secrets in `settings`, `application_state`, source code, or action responses.
169
+ Never store secrets in `settings`, `application_state`, source code, or action responses. Use the credential / vault APIs above — they handle both encryption and per-user scoping.
151
170
 
152
171
  ## Authentication {#auth}
153
172
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.28.2",
3
+ "version": "0.28.3",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22"
@@ -296,6 +296,7 @@
296
296
  "@types/react": "^19.2.14",
297
297
  "@types/ws": "^8.18.1",
298
298
  "@vitejs/plugin-react-swc": "^4.0.0",
299
+ "@vitest/coverage-v8": "4.1.5",
299
300
  "@xterm/addon-fit": "^0.11.0",
300
301
  "@xterm/addon-web-links": "^0.12.0",
301
302
  "@xterm/xterm": "^6.0.0",
@@ -93,6 +93,7 @@ You do NOT get auto-injected screen state. **Call `pnpm action view-screen` at t
93
93
  - Use `db-exec UPDATE` only when no domain action exists and you need a small ad-hoc change.
94
94
  - Use `db-patch` when you only need to tweak a small slice of a **large** text/JSON column (documents, slide HTML, dashboard/form JSON). It saves tokens by sending `{find, replace}` instead of re-transmitting the whole column. Targets exactly one row per call — narrow `--where` by primary key. Supports `--edits '[{find,replace},...]'` for batch edits and `--all` for replace-every-occurrence.
95
95
  - If a template-specific action exists (e.g. `edit-document`, `update-slide`), prefer it — those also push live updates to any open collaborative editor.
96
+ - **Database admin (dev only):** in development, `db-admin-query` / `db-admin-mutate` / `db-admin-rows` / `db-admin-tables` / `db-admin-schema` give **unscoped, full-database** access to ANY table — including framework tables and tables without `owner_email`/`org_id`. Prefer these over `db-exec`/`db-query` for database-admin work and for any non-owner-scoped table: `db-exec`/`db-query` auto-scope to the current user and return **0 rows** on unscoped tables. These mirror the in-app Database admin UI, so prompts and the UI do the same thing.
96
97
 
97
98
  ## Skills
98
99