@arcjet/astro 1.2.0 → 1.3.1

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.
package/README.md CHANGED
@@ -16,102 +16,555 @@
16
16
  </a>
17
17
  </p>
18
18
 
19
- [Arcjet][arcjet] helps developers protect their apps in just a few lines of
20
- code. Implement rate limiting, bot protection, email verification, and defense
21
- against common attacks.
19
+ [Arcjet][arcjet] is the runtime security platform that ships with your AI code. Stop bots and automated attacks from burning your AI budget, leaking data, or misusing tools with Arcjet's AI security building blocks. Every feature works with any Astro application.
22
20
 
23
- This is the [Arcjet][arcjet] SDK integration for [Astro][astro].
24
-
25
- - [npm package (`@arcjet/astro`)](https://www.npmjs.com/package/@arcjet/astro)
26
- - [GitHub source code (`arcjet-astro/` in `arcjet/arcjet-js`)](https://github.com/arcjet/arcjet-js/tree/main/arcjet-astro)
21
+ This is the [Arcjet][arcjet] SDK for [Astro][astro].
27
22
 
28
23
  ## Getting started
29
24
 
30
- Visit the [quick start guide][quick-start] to get started.
25
+ 1. Get your API key at [`app.arcjet.com`](https://app.arcjet.com)
26
+ 2. `npm install @arcjet/astro`
27
+ 3. Set `ARCJET_KEY=ajkey_yourkey` in your environment
28
+ 4. Add Arcjet to your app — see the [quick start](#quick-start) below
31
29
 
32
- ## Example
30
+ [npm package](https://www.npmjs.com/package/@arcjet/astro) |
31
+ [GitHub source](https://github.com/arcjet/arcjet-js/tree/main/arcjet-astro) |
32
+ [Full docs][arcjet-reference-astro] |
33
+ [Other SDKs on GitHub](https://github.com/arcjet)
33
34
 
34
- Try an Arcjet protected Next.js app live at
35
- [`example.arcjet.com`][example-next-url]
36
- ([source code][example-next-source]).
37
- See [`arcjet/example-astro`][example-astro-source] for an Astro example.
35
+ ## Features
38
36
 
39
- ## What is this?
37
+ - 🔒 [Prompt Injection Detection](#prompt-injection-detection) — detect and block
38
+ prompt injection attacks before they reach your LLM.
39
+ - 🤖 [Bot Protection](#bot-protection) — stop scrapers, credential stuffers,
40
+ and AI crawlers from abusing your endpoints.
41
+ - 🛑 [Rate Limiting](#rate-limiting) — token bucket, fixed window, and sliding
42
+ window algorithms; model AI token budgets per user.
43
+ - 🕵️ [Sensitive Information Detection](#sensitive-information-detection) — block
44
+ PII, credit cards, and custom patterns from entering your AI pipeline.
45
+ - 🛡️ [Shield WAF](#shield-waf) — protect against SQL injection, XSS, and other
46
+ common web attacks.
47
+ - 📧 [Email Validation](#email-validation) — block disposable, invalid, and
48
+ undeliverable addresses at signup.
49
+ - 📝 [Signup Form Protection][signup-protection-docs] — combines bot protection,
50
+ email validation, and rate limiting to protect your signup forms.
51
+ - 🎯 [Request Filters](#request-filters) — expression-based rules on IP, path,
52
+ headers, and custom fields.
53
+ - 🌐 [IP Analysis](#ip-analysis) — geolocation, ASN, VPN, proxy, Tor, and hosting
54
+ detection included with every request.
40
55
 
41
- This is our adapter to integrate Arcjet into Astro.
42
- Arcjet helps you secure your Astro website.
43
- This package exists so that we can provide the best possible experience to
44
- Astro users.
56
+ ## Quick start
45
57
 
46
- ## When should I use this?
58
+ This example protects an Astro API route with bot detection, Shield WAF,
59
+ and token bucket rate limiting.
47
60
 
48
- You can use this if you are using Astro.
49
- See our [_Get started_ guide][arcjet-get-started] for other supported
50
- frameworks.
61
+ First, configure Arcjet in `astro.config.mjs`:
51
62
 
52
- ## Install
63
+ ```js
64
+ // astro.config.mjs
65
+ import arcjet, { detectBot, shield, tokenBucket } from "@arcjet/astro";
66
+ import { defineConfig } from "astro/config";
53
67
 
54
- This package is ESM only.
55
- Install with npm and the Astro CLI in Node.js:
68
+ export default defineConfig({
69
+ env: { validateSecrets: true },
70
+ integrations: [
71
+ arcjet({
72
+ rules: [
73
+ // Shield protects your app from common attacks e.g. SQL injection
74
+ shield({ mode: "LIVE" }),
75
+ // Create a bot detection rule
76
+ detectBot({
77
+ mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
78
+ // Block all bots except the following
79
+ allow: [
80
+ "CATEGORY:SEARCH_ENGINE", // Google, Bing, etc
81
+ // Uncomment to allow these other common bot categories
82
+ // See the full list at https://arcjet.com/bot-list
83
+ //"CATEGORY:MONITOR", // Uptime monitoring services
84
+ //"CATEGORY:PREVIEW", // Link previews e.g. Slack, Discord
85
+ ],
86
+ }),
87
+ // Token bucket rate limit. Other algorithms are supported.
88
+ tokenBucket({
89
+ mode: "LIVE",
90
+ refillRate: 5, // Refill 5 tokens per interval
91
+ interval: 10, // Refill every 10 seconds
92
+ capacity: 10, // Bucket capacity of 10 tokens
93
+ }),
94
+ ],
95
+ }),
96
+ ],
97
+ });
98
+ ```
99
+
100
+ Then use it in an API route (e.g. `src/pages/api/hello.ts`):
101
+
102
+ ```ts
103
+ // src/pages/api/hello.ts
104
+ import { isSpoofedBot } from "@arcjet/inspect";
105
+ import type { APIRoute } from "astro";
106
+ import aj from "arcjet:client";
56
107
 
57
- ```sh
58
- npx astro add @arcjet/astro
108
+ export const GET: APIRoute = async ({ request }) => {
109
+ const decision = await aj.protect(request, { requested: 5 }); // Deduct 5 tokens
110
+ console.log("Arcjet decision", decision);
111
+
112
+ if (decision.isDenied()) {
113
+ if (decision.reason.isRateLimit()) {
114
+ return Response.json({ error: "Too many requests" }, { status: 429 });
115
+ } else if (decision.reason.isBot()) {
116
+ return Response.json({ error: "No bots allowed" }, { status: 403 });
117
+ } else {
118
+ return Response.json({ error: "Forbidden" }, { status: 403 });
119
+ }
120
+ }
121
+
122
+ // Requests from hosting IPs are likely from bots.
123
+ // https://docs.arcjet.com/blueprints/vpn-proxy-detection
124
+ if (decision.ip.isHosting()) {
125
+ return Response.json({ error: "Forbidden" }, { status: 403 });
126
+ }
127
+
128
+ // Verifies the authenticity of common bots using IP data.
129
+ // Verification isn't always possible, so check the results separately.
130
+ // https://docs.arcjet.com/bot-protection/reference#bot-verification
131
+ if (decision.results.some(isSpoofedBot)) {
132
+ return Response.json({ error: "Forbidden" }, { status: 403 });
133
+ }
134
+
135
+ return Response.json({ message: "Hello world" });
136
+ };
59
137
  ```
60
138
 
61
- ## Use
139
+ For the full reference, see the [Arcjet Astro SDK docs][arcjet-reference-astro].
140
+
141
+ ## Prompt injection detection
62
142
 
63
- Configure Arcjet in `astro.config.mjs`:
143
+ Detect and block prompt injection attacks — attempts to override your AI
144
+ model's instructions — before they reach your model. Pass the user's message
145
+ via `detectPromptInjectionMessage` on each `protect()` call. Tune sensitivity with the `threshold` parameter (0.0–1.0, default 0.5) — higher values are more conservative.
146
+
147
+ Configure in `astro.config.mjs`:
64
148
 
65
149
  ```js
66
- import arcjet, { shield } from "@arcjet/astro";
150
+ import arcjet, { detectPromptInjection } from "@arcjet/astro";
67
151
  import { defineConfig } from "astro/config";
68
152
 
69
153
  export default defineConfig({
70
- // We recommend setting
71
- // [`validateSecrets`](https://docs.astro.build/en/reference/configuration-reference/#envvalidatesecrets).
72
- env: { validateSecrets: true },
73
154
  integrations: [
74
155
  arcjet({
75
156
  rules: [
76
- // Shield protects your app from common attacks.
77
- // Use `DRY_RUN` instead of `LIVE` to only log.
78
- shield({ mode: "LIVE" }),
157
+ detectPromptInjection({
158
+ mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
159
+ threshold: 0.5, // Score above which requests are blocked (default: 0.5)
160
+ }),
79
161
  ],
80
162
  }),
81
163
  ],
82
164
  });
83
165
  ```
84
166
 
85
- …then use Arcjet in on-demand routes (such as `src/pages/api.json.ts`):
167
+ Then in an API route:
86
168
 
87
169
  ```ts
88
170
  import type { APIRoute } from "astro";
89
171
  import aj from "arcjet:client";
90
172
 
91
- export const GET: APIRoute = async ({ request }) => {
92
- const decision = await aj.protect(request);
173
+ export const POST: APIRoute = async ({ request }) => {
174
+ const { message } = await request.json();
93
175
 
94
- if (decision.isDenied()) {
95
- return Response.json({ message: "Forbidden" }, { status: 403 });
176
+ const decision = await aj.protect(request, {
177
+ detectPromptInjectionMessage: message,
178
+ });
179
+
180
+ if (decision.isDenied() && decision.reason.isPromptInjection()) {
181
+ return Response.json(
182
+ { error: "Prompt injection detected — please rephrase your message" },
183
+ { status: 400 },
184
+ );
96
185
  }
97
186
 
98
- return Response.json({ message: "Hello world" });
187
+ // Forward to your AI model...
188
+ };
189
+ ```
190
+
191
+ ## Bot protection
192
+
193
+ Arcjet allows you to configure a list of bots to allow or deny. Specifying
194
+ `allow` means all other bots are denied. An empty allow list blocks all bots.
195
+
196
+ Available categories: `CATEGORY:ACADEMIC`, `CATEGORY:ADVERTISING`,
197
+ `CATEGORY:AI`, `CATEGORY:AMAZON`, `CATEGORY:APPLE`, `CATEGORY:ARCHIVE`,
198
+ `CATEGORY:BOTNET`, `CATEGORY:FEEDFETCHER`, `CATEGORY:GOOGLE`,
199
+ `CATEGORY:META`, `CATEGORY:MICROSOFT`, `CATEGORY:MONITOR`,
200
+ `CATEGORY:OPTIMIZER`, `CATEGORY:PREVIEW`, `CATEGORY:PROGRAMMATIC`,
201
+ `CATEGORY:SEARCH_ENGINE`, `CATEGORY:SLACK`, `CATEGORY:SOCIAL`,
202
+ `CATEGORY:TOOL`, `CATEGORY:UNKNOWN`, `CATEGORY:VERCEL`,
203
+ `CATEGORY:WEBHOOK`, `CATEGORY:YAHOO`. You can also allow or deny
204
+ [specific bots by name][bot-list].
205
+
206
+ ```js
207
+ import arcjet, { detectBot } from "@arcjet/astro";
208
+
209
+ // In astro.config.mjs:
210
+ arcjet({
211
+ rules: [
212
+ detectBot({
213
+ mode: "LIVE",
214
+ allow: [
215
+ "CATEGORY:SEARCH_ENGINE",
216
+ // See the full list at https://arcjet.com/bot-list
217
+ ],
218
+ }),
219
+ ],
220
+ });
221
+ ```
222
+
223
+ ```ts
224
+ // In your API route:
225
+ import { isSpoofedBot } from "@arcjet/inspect";
226
+ import aj from "arcjet:client";
227
+
228
+ const decision = await aj.protect(request);
229
+
230
+ if (decision.isDenied() && decision.reason.isBot()) {
231
+ return Response.json({ error: "No bots allowed" }, { status: 403 });
232
+ }
233
+
234
+ // Verifies the authenticity of common bots using IP data.
235
+ if (decision.results.some(isSpoofedBot)) {
236
+ return Response.json({ error: "Forbidden" }, { status: 403 });
237
+ }
238
+ ```
239
+
240
+ ### Bot categories
241
+
242
+ Bots can be configured by [category][bot-categories-docs] and/or by [specific
243
+ bot name][bot-list]. For example, to allow search engines and the OpenAI
244
+ crawler, but deny all other bots:
245
+
246
+ ```js
247
+ detectBot({
248
+ mode: "LIVE",
249
+ allow: ["CATEGORY:SEARCH_ENGINE", "OPENAI_CRAWLER_SEARCH"],
250
+ });
251
+ ```
252
+
253
+ ### Verified vs spoofed bots
254
+
255
+ Bots claiming to be well-known crawlers (e.g. Googlebot) are verified by
256
+ checking their IP address against known IP ranges. If a bot fails verification,
257
+ it is labeled as spoofed. Use `isSpoofedBot` from `@arcjet/inspect` to check:
258
+
259
+ ```ts
260
+ import { isSpoofedBot } from "@arcjet/inspect";
261
+
262
+ if (decision.results.some(isSpoofedBot)) {
263
+ return Response.json({ error: "Forbidden" }, { status: 403 });
264
+ }
265
+ ```
266
+
267
+ ## Rate limiting
268
+
269
+ Arcjet supports token bucket, fixed window, and sliding window algorithms.
270
+ Token buckets are ideal for controlling AI token budgets — set `capacity` to
271
+ the max tokens a user can spend, `refillRate` to how many tokens are restored
272
+ per `interval`, and deduct tokens per request via `requested` in `protect()`.
273
+ The `interval` accepts strings (`"1s"`, `"1m"`, `"1h"`, `"1d"`) or seconds as
274
+ a number. Use `characteristics` to track limits per user instead of per IP.
275
+
276
+ ```js
277
+ import arcjet, { tokenBucket } from "@arcjet/astro";
278
+
279
+ // In astro.config.mjs:
280
+ arcjet({
281
+ characteristics: ["userId"], // Track per user
282
+ rules: [
283
+ tokenBucket({
284
+ mode: "LIVE",
285
+ refillRate: 2_000, // Refill 2,000 tokens per hour
286
+ interval: "1h",
287
+ capacity: 5_000, // Maximum 5,000 tokens in the bucket
288
+ }),
289
+ ],
290
+ });
291
+ ```
292
+
293
+ ```ts
294
+ // In your API route:
295
+ const decision = await aj.protect(request, {
296
+ userId: "user-123",
297
+ requested: estimate, // Number of tokens to deduct
298
+ });
299
+
300
+ if (decision.isDenied() && decision.reason.isRateLimit()) {
301
+ return Response.json({ error: "Rate limit exceeded" }, { status: 429 });
302
+ }
303
+ ```
304
+
305
+ ## Sensitive information detection
306
+
307
+ Detect and block PII in request content. Pass the content to scan via
308
+ `sensitiveInfoValue` on each `protect()` call. Built-in entity types:
309
+ `CREDIT_CARD_NUMBER`, `EMAIL`, `PHONE_NUMBER`, `IP_ADDRESS`. You can also
310
+ provide a custom `detect` callback for additional patterns.
311
+
312
+ ```js
313
+ import arcjet, { sensitiveInfo } from "@arcjet/astro";
314
+
315
+ // In astro.config.mjs:
316
+ arcjet({
317
+ rules: [
318
+ sensitiveInfo({
319
+ mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
320
+ deny: ["CREDIT_CARD_NUMBER", "EMAIL", "PHONE_NUMBER"],
321
+ }),
322
+ ],
323
+ });
324
+ ```
325
+
326
+ ```ts
327
+ // In your API route:
328
+ const decision = await aj.protect(request, {
329
+ sensitiveInfoValue: userMessage, // The text content to scan
330
+ });
331
+
332
+ if (decision.isDenied() && decision.reason.isSensitiveInfo()) {
333
+ return Response.json(
334
+ { error: "Sensitive information detected" },
335
+ { status: 400 },
336
+ );
337
+ }
338
+ ```
339
+
340
+ ## Shield WAF
341
+
342
+ Protect your application against common web attacks, including the OWASP
343
+ Top 10.
344
+
345
+ ```js
346
+ import arcjet, { shield } from "@arcjet/astro";
347
+ import { defineConfig } from "astro/config";
348
+
349
+ export default defineConfig({
350
+ integrations: [
351
+ arcjet({
352
+ rules: [
353
+ shield({
354
+ mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
355
+ }),
356
+ ],
357
+ }),
358
+ ],
359
+ });
360
+ ```
361
+
362
+ ## Email validation
363
+
364
+ Validate and verify email addresses. Deny types: `DISPOSABLE`, `FREE`,
365
+ `NO_MX_RECORDS`, `NO_GRAVATAR`, `INVALID`.
366
+
367
+ ```js
368
+ import arcjet, { validateEmail } from "@arcjet/astro";
369
+ import { defineConfig } from "astro/config";
370
+
371
+ export default defineConfig({
372
+ integrations: [
373
+ arcjet({
374
+ rules: [
375
+ validateEmail({
376
+ mode: "LIVE",
377
+ deny: ["DISPOSABLE", "INVALID", "NO_MX_RECORDS"],
378
+ }),
379
+ ],
380
+ }),
381
+ ],
382
+ });
383
+ ```
384
+
385
+ ```ts
386
+ // In your API route:
387
+ const decision = await aj.protect(request, {
388
+ email: "user@example.com",
389
+ });
390
+
391
+ if (decision.isDenied() && decision.reason.isEmail()) {
392
+ return Response.json({ error: "Invalid email address" }, { status: 400 });
393
+ }
394
+ ```
395
+
396
+ ## Request filters
397
+
398
+ Filter requests using expression-based rules against request properties (IP,
399
+ headers, path, method, etc.).
400
+
401
+ ```js
402
+ import arcjet, { filter } from "@arcjet/astro";
403
+
404
+ // In astro.config.mjs:
405
+ arcjet({
406
+ rules: [
407
+ filter({
408
+ mode: "LIVE",
409
+ deny: ['ip.src == "1.2.3.4"'],
410
+ }),
411
+ ],
412
+ });
413
+ ```
414
+
415
+ ### Block by country
416
+
417
+ Restrict access to specific countries — useful for licensing, compliance, or
418
+ regional rollouts. The `allow` list denies all countries not listed:
419
+
420
+ ```js
421
+ filter({
422
+ mode: "LIVE",
423
+ // Allow only US traffic — all other countries are denied
424
+ allow: ['ip.src.country == "US"'],
425
+ });
426
+ ```
427
+
428
+ ### Block VPN and proxy traffic
429
+
430
+ Prevent anonymized traffic from accessing sensitive endpoints — useful for
431
+ fraud prevention, enforcing geo-restrictions, and reducing abuse:
432
+
433
+ ```js
434
+ filter({
435
+ mode: "LIVE",
436
+ deny: [
437
+ "ip.src.vpn", // VPN services
438
+ "ip.src.proxy", // Open proxies
439
+ "ip.src.tor", // Tor exit nodes
440
+ ],
441
+ });
442
+ ```
443
+
444
+ For more nuanced handling, use `decision.ip` helpers after calling `protect()`:
445
+
446
+ ```ts
447
+ const decision = await aj.protect(request);
448
+
449
+ if (decision.ip.isVpn() || decision.ip.isTor()) {
450
+ return Response.json({ error: "VPN traffic not allowed" }, { status: 403 });
451
+ }
452
+ ```
453
+
454
+ See the [Request Filters docs][filters-docs],
455
+ [IP Geolocation blueprint](https://docs.arcjet.com/blueprints/ip-geolocation), and
456
+ [VPN/Proxy Detection blueprint](https://docs.arcjet.com/blueprints/vpn-proxy-detection)
457
+ for more details.
458
+
459
+ ## IP analysis
460
+
461
+ Arcjet enriches every request with IP metadata. Use these helpers to make
462
+ policy decisions based on network signals:
463
+
464
+ ```ts
465
+ const decision = await aj.protect(request);
466
+
467
+ if (decision.ip.isHosting()) {
468
+ // Requests from cloud/hosting providers are often automated.
469
+ // https://docs.arcjet.com/blueprints/vpn-proxy-detection
470
+ return Response.json({ error: "Forbidden" }, { status: 403 });
471
+ }
472
+
473
+ if (decision.ip.isVpn() || decision.ip.isProxy() || decision.ip.isTor()) {
474
+ // Handle VPN/proxy traffic according to your policy
475
+ }
476
+
477
+ // Access geolocation and network details
478
+ console.log(decision.ip.country, decision.ip.city, decision.ip.asn);
479
+ ```
480
+
481
+ ## Custom characteristics
482
+
483
+ Track and limit requests by any stable identifier — user ID, API key, session,
484
+ etc. — rather than IP address alone.
485
+
486
+ ```js
487
+ // In astro.config.mjs:
488
+ arcjet({
489
+ characteristics: ["userId"], // Declare at the SDK level
490
+ rules: [
491
+ tokenBucket({
492
+ mode: "LIVE",
493
+ refillRate: 2_000,
494
+ interval: "1h",
495
+ capacity: 5_000,
496
+ }),
497
+ ],
498
+ });
499
+ ```
500
+
501
+ ```ts
502
+ // Pass the characteristic value at request time
503
+ const decision = await aj.protect(request, {
504
+ userId: "user-123", // Replace with your actual user ID
505
+ requested: estimate,
506
+ });
507
+ ```
508
+
509
+ ## Best practices
510
+
511
+ See the [Arcjet best practices][best-practices] for detailed guidance. Key
512
+ recommendations:
513
+
514
+ **Use `withRule()` for route-specific rules** on top of the base rules
515
+ configured in `astro.config.mjs`. The SDK caches decisions and configuration,
516
+ so this is more efficient than creating a new instance per request.
517
+
518
+ ```ts
519
+ // src/pages/api/chat.ts — extend with withRule()
520
+ import { detectBot, tokenBucket } from "@arcjet/astro";
521
+ import { isSpoofedBot } from "@arcjet/inspect";
522
+ import type { APIRoute } from "astro";
523
+ import aj from "arcjet:client";
524
+
525
+ const routeAj = aj.withRule(detectBot({ mode: "LIVE", allow: [] })).withRule(
526
+ tokenBucket({
527
+ mode: "LIVE",
528
+ refillRate: 2_000,
529
+ interval: "1h",
530
+ capacity: 5_000,
531
+ }),
532
+ );
533
+
534
+ export const POST: APIRoute = async ({ request }) => {
535
+ const decision = await routeAj.protect(request, { requested: 500 });
536
+ // ...
99
537
  };
100
538
  ```
101
539
 
102
- For more on how to configure Arcjet with Astro and how to protect Astro,
103
- see the [Arcjet Astro SDK reference][arcjet-reference-astro] on our website.
540
+ **Other recommendations:**
541
+
542
+ - **Start rules in `DRY_RUN` mode** to observe behavior before switching to
543
+ `LIVE`. This lets you tune thresholds without affecting real traffic.
544
+ - **Configure proxies** if your app runs behind a load balancer or reverse proxy
545
+ so Arcjet resolves the real client IP:
546
+ ```js
547
+ arcjet({ rules: [], proxies: ["100.100.100.100"] });
548
+ ```
549
+ - **Handle errors explicitly.** `protect()` never throws — on error it returns
550
+ an `ERROR` result. Fail open by logging and allowing the request:
551
+ ```ts
552
+ if (decision.isErrored()) {
553
+ console.error("Arcjet error", decision.reason.message);
554
+ // allow the request to proceed
555
+ }
556
+ ```
104
557
 
105
558
  ## License
106
559
 
107
560
  [Apache License, Version 2.0][apache-license] © [Arcjet Labs, Inc.][arcjet]
108
561
 
109
- [arcjet-get-started]: https://docs.arcjet.com/get-started
110
- [arcjet-reference-astro]: https://docs.arcjet.com/reference/astro
111
562
  [arcjet]: https://arcjet.com
563
+ [arcjet-reference-astro]: https://docs.arcjet.com/reference/astro
112
564
  [astro]: https://astro.build/
113
- [example-astro-source]: https://github.com/arcjet/example-astro
114
- [example-next-source]: https://github.com/arcjet/example-nextjs
115
- [example-next-url]: https://example.arcjet.com
116
- [quick-start]: https://docs.arcjet.com/get-started/astro
117
565
  [apache-license]: http://www.apache.org/licenses/LICENSE-2.0
566
+ [bot-categories-docs]: https://docs.arcjet.com/bot-protection/identifying-bots
567
+ [bot-list]: https://arcjet.com/bot-list
568
+ [signup-protection-docs]: https://docs.arcjet.com/signup-protection
569
+ [filters-docs]: https://docs.arcjet.com/filters
570
+ [best-practices]: https://docs.arcjet.com/best-practices
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BotOptions, EmailOptions, FilterOptions, FixedWindowRateLimitOptions, ProtectSignupOptions, SensitiveInfoOptions, ShieldOptions, SlidingWindowRateLimitOptions, TokenBucketRateLimitOptions } from "arcjet";
1
+ import type { BotOptions, DetectPromptInjectionOptions, EmailOptions, FilterOptions, FixedWindowRateLimitOptions, ProtectSignupOptions, SensitiveInfoOptions, ShieldOptions, SlidingWindowRateLimitOptions, TokenBucketRateLimitOptions } from "arcjet";
2
2
  import type { AstroIntegration } from "astro";
3
3
  type IntegrationRule<Characteristics extends readonly string[]> = {
4
4
  type: "shield";
@@ -27,6 +27,9 @@ type IntegrationRule<Characteristics extends readonly string[]> = {
27
27
  } | {
28
28
  type: "protectSignup";
29
29
  options: ProtectSignupOptions<Characteristics>;
30
+ } | {
31
+ type: "detectPromptInjection";
32
+ options: DetectPromptInjectionOptions;
30
33
  };
31
34
  /**
32
35
  * Configuration for the Astro integration of Arcjet.
@@ -286,6 +289,31 @@ export declare function protectSignup<Characteristics extends readonly string[]>
286
289
  readonly type: "protectSignup";
287
290
  readonly options: ProtectSignupOptions<Characteristics>;
288
291
  };
292
+ /**
293
+ * Arcjet prompt injection detection rule.
294
+ *
295
+ * Analyzes LLM prompts to detect prompt injection attempts.
296
+ *
297
+ * The Arcjet prompt injection detection rule analyzes prompts sent to LLM
298
+ * applications to detect jailbreak and injection attempts.
299
+ * The analysis is performed through Arcjet's Cloud API.
300
+ *
301
+ * @param options
302
+ * Configuration for the prompt injection detection rule.
303
+ * @returns
304
+ * Astro integration Prompt injection detection rule to provide to the SDK in the `rules` field.
305
+ */
306
+ export declare function detectPromptInjection(options?: DetectPromptInjectionOptions): {
307
+ readonly type: "detectPromptInjection";
308
+ readonly options: DetectPromptInjectionOptions;
309
+ };
310
+ /**
311
+ * Arcjet prompt injection detection rule.
312
+ *
313
+ * @deprecated
314
+ * Use `detectPromptInjection` instead.
315
+ */
316
+ export declare const experimental_detectPromptInjection: typeof detectPromptInjection;
289
317
  /**
290
318
  * Configuration for {@linkcode createRemoteClient}.
291
319
  */
package/index.js CHANGED
@@ -113,6 +113,12 @@ const validateProtectSignupOptions = z
113
113
  email: validateEmailOptions,
114
114
  })
115
115
  .strict();
116
+ const validateDetectPromptInjectionOptions = z
117
+ .object({
118
+ mode: validateMode.optional(),
119
+ threshold: z.number().optional(),
120
+ })
121
+ .strict();
116
122
  function validateAndSerialize(schema, value) {
117
123
  const v = schema.parse(value);
118
124
  return v ? JSON.stringify(v) : "";
@@ -182,6 +188,13 @@ function integrationRuleToClientRule(rule) {
182
188
  code: `protectSignup(${serializedOpts})`,
183
189
  };
184
190
  }
191
+ case "detectPromptInjection": {
192
+ const serializedOpts = validateAndSerialize(validateDetectPromptInjectionOptions, rule.options);
193
+ return {
194
+ importName: `detectPromptInjection`,
195
+ code: `detectPromptInjection(${serializedOpts})`,
196
+ };
197
+ }
185
198
  default: {
186
199
  throw new Error("Cannot convert rule via integration");
187
200
  }
@@ -428,6 +441,34 @@ function protectSignup(options) {
428
441
  options,
429
442
  };
430
443
  }
444
+ // Note: please keep JSDocs in sync with `arcjet` core.
445
+ /**
446
+ * Arcjet prompt injection detection rule.
447
+ *
448
+ * Analyzes LLM prompts to detect prompt injection attempts.
449
+ *
450
+ * The Arcjet prompt injection detection rule analyzes prompts sent to LLM
451
+ * applications to detect jailbreak and injection attempts.
452
+ * The analysis is performed through Arcjet's Cloud API.
453
+ *
454
+ * @param options
455
+ * Configuration for the prompt injection detection rule.
456
+ * @returns
457
+ * Astro integration Prompt injection detection rule to provide to the SDK in the `rules` field.
458
+ */
459
+ function detectPromptInjection(options = {}) {
460
+ return {
461
+ type: "detectPromptInjection",
462
+ options,
463
+ };
464
+ }
465
+ /**
466
+ * Arcjet prompt injection detection rule.
467
+ *
468
+ * @deprecated
469
+ * Use `detectPromptInjection` instead.
470
+ */
471
+ const experimental_detectPromptInjection = detectPromptInjection;
431
472
  /**
432
473
  * Create a remote client.
433
474
  *
@@ -626,4 +667,4 @@ function arcjet(options = { rules: [] }) {
626
667
  };
627
668
  }
628
669
 
629
- export { createRemoteClient, arcjet as default, detectBot, filter, fixedWindow, protectSignup, sensitiveInfo, shield, slidingWindow, tokenBucket, validateEmail };
670
+ export { createRemoteClient, arcjet as default, detectBot, detectPromptInjection, experimental_detectPromptInjection, filter, fixedWindow, protectSignup, sensitiveInfo, shield, slidingWindow, tokenBucket, validateEmail };
package/internal.js CHANGED
@@ -40,7 +40,7 @@ function createRemoteClient(options) {
40
40
  // Transport is the HTTP client that the client uses to make requests.
41
41
  const transport = createTransport(url);
42
42
  const sdkStack = "ASTRO";
43
- const sdkVersion = "1.2.0";
43
+ const sdkVersion = "1.3.1";
44
44
  return createClient({
45
45
  transport,
46
46
  baseUrl: url,
package/package.json CHANGED
@@ -1,18 +1,25 @@
1
1
  {
2
2
  "name": "@arcjet/astro",
3
- "version": "1.2.0",
4
- "description": "Arcjet helps developers protect their Astro sites in just a few lines of code. Bot detection. Rate limiting. Email validation. Attack protection. Data redaction. A developer-first approach to security.",
3
+ "version": "1.3.1",
4
+ "description": "Arcjet runtime security SDK for Astro bot protection, rate limiting, prompt injection detection, PII blocking, and WAF",
5
5
  "keywords": [
6
+ "ai",
6
7
  "analyze",
7
8
  "arcjet",
8
- "astro-integration",
9
9
  "astro",
10
+ "astro-integration",
10
11
  "attack",
12
+ "bot-detection",
11
13
  "limit",
14
+ "llm",
15
+ "pii",
16
+ "prompt-injection",
12
17
  "protect",
18
+ "rate-limiting",
13
19
  "secure",
14
20
  "security",
15
21
  "verify",
22
+ "waf",
16
23
  "withastro"
17
24
  ],
18
25
  "license": "Apache-2.0",
@@ -51,24 +58,24 @@
51
58
  "test": "npm run build && npm run lint && npm run test-coverage"
52
59
  },
53
60
  "dependencies": {
54
- "@arcjet/body": "1.2.0",
55
- "@arcjet/env": "1.2.0",
56
- "@arcjet/headers": "1.2.0",
57
- "@arcjet/ip": "1.2.0",
58
- "@arcjet/logger": "1.2.0",
59
- "@arcjet/protocol": "1.2.0",
60
- "@arcjet/transport": "1.2.0",
61
- "arcjet": "1.2.0"
61
+ "@arcjet/body": "1.3.1",
62
+ "@arcjet/env": "1.3.1",
63
+ "@arcjet/headers": "1.3.1",
64
+ "@arcjet/ip": "1.3.1",
65
+ "@arcjet/logger": "1.3.1",
66
+ "@arcjet/protocol": "1.3.1",
67
+ "@arcjet/transport": "1.3.1",
68
+ "arcjet": "1.3.1"
62
69
  },
63
70
  "peerDependencies": {
64
- "astro": "^5.9.3"
71
+ "astro": "^5.9.3 || ^6.0.0"
65
72
  },
66
73
  "devDependencies": {
67
- "@arcjet/eslint-config": "1.2.0",
68
- "@arcjet/rollup-config": "1.2.0",
69
- "@rollup/wasm-node": "4.57.1",
70
- "astro": "5.17.1",
71
- "eslint": "9.39.2",
74
+ "@arcjet/eslint-config": "1.3.1",
75
+ "@arcjet/rollup-config": "1.3.1",
76
+ "@rollup/wasm-node": "4.59.0",
77
+ "astro": "6.1.2",
78
+ "eslint": "9.39.3",
72
79
  "typescript": "5.9.3"
73
80
  },
74
81
  "publishConfig": {