Arcjet decision inspection reference
The Arcjet decision inspection helpers work with the decision returned from an Arcjet SDK.
What are Arcjet utilities?
Arcjet utilities are independent libraries that do not require the use of the main Arcjet SDK - they can be used with or without other Arcjet rules.
We take the pain out of implementing security tasks through these utilities to provide a security as code approach to developer-first security.
In JavaScript they ship as the @arcjet/inspect package. In Python they are
exported from the arcjet package – no extra install.
Why inspect a decision
Section titled “Why inspect a decision”In addition to providing an easy-to-consume security recommendation, each Arcjet SDK also provides a lot of metadata attached to every decision. You can use all of these signals to inform application logic, but extracting the information can be verbose.
As we notice common patterns, we provide optional utilities to streamline these operations.
Install
Section titled “Install”npm install -S @arcjet/inspectThe helpers ship in the arcjet package. Import them after you install the
SDK:
from arcjet import is_missing_user_agent, is_spoofed_bot, is_verified_bot// Replace with the framework SDK you're using e.g. `@arcjet/node`import arcjet, { detectBot } from "@arcjet/next";import { isVerifiedBot, isSpoofedBot, isMissingUserAgent,} from "@arcjet/inspect";import type { NextApiRequest, NextApiResponse } from "next";
const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ detectBot({ mode: "LIVE", allow: ["CATEGORY:SEARCH_ENGINE"], }), ],});
export default async function handler( req: NextApiRequest, res: NextApiResponse,) { const decision = await aj.protect(req);
// Allow any verified search engine bot without considering any other signals if (decision.results.some(isVerifiedBot)) { return res .status(200) .json({ name: "Hello bot! Here's some SEO optimized response" }); }
// Block a request if the SDK suggests it if (decision.isDenied()) { return res.status(403).json({ error: "Forbidden" }); }
// Block any request without a User-Agent header because we expect all // well-behaved clients to have it if (decision.results.some(isMissingUserAgent)) { return res.status(400).json({ error: "You are a bot!" }); }
// Block any client pretending to be a search engine bot but using an IP // address that doesn't satisfy the verification if (decision.results.some(isSpoofedBot)) { return res .status(403) .json({ error: "You are pretending to be a good bot!" }); }
res.status(200).json({ name: "Hello world" });}import os
from arcjet import ( Mode, arcjet, detect_bot, is_missing_user_agent, is_spoofed_bot, is_verified_bot,)from fastapi import FastAPI, Requestfrom fastapi.responses import JSONResponse
app = FastAPI()
aj = arcjet( key=os.environ["ARCJET_KEY"], rules=[ detect_bot( mode=Mode.LIVE, allow=["CATEGORY:SEARCH_ENGINE"], ), ],)
@app.get("/")async def index(request: Request): decision = await aj.protect(request)
# Allow any verified search engine bot without considering any other # signals if any(is_verified_bot(r) for r in decision.results): return { "name": "Hello bot! Here's some SEO optimized response" }
# Block a request if the SDK suggests it if decision.is_denied(): return JSONResponse({"error": "Forbidden"}, status_code=403)
# Block any request without a User-Agent header because we expect all # well-behaved clients to have it if any(is_missing_user_agent(r) for r in decision.results): return JSONResponse({"error": "You are a bot!"}, status_code=400)
# Block any client pretending to be a search engine bot but using an # IP address that doesn't satisfy the verification if any(is_spoofed_bot(r) for r in decision.results): return JSONResponse( {"error": "You are pretending to be a good bot!"}, status_code=403, )
return {"name": "Hello world"}isSpoofedBot / is_spoofed_bot
Section titled “isSpoofedBot / is_spoofed_bot”Determines whether a non-"DRY_RUN" bot rule detected a spoofed request. If
the helper reports a spoofed bot, the request was likely spoofed and you may
want to block it.
For allow rules, Arcjet verifies the authenticity of detected bots by checking
IP data and performing reverse DNS lookups. This helps protect against spoofed
bots where malicious clients pretend to be a well-behaving bot.
isSpoofedBot(result: ArcjetRuleResult) from @arcjet/inspect.
Returns one of the following:
trueif the bot rule result was not"DRY_RUN"and Arcjet detected a spoofed bot.falseif the bot rule result was not"DRY_RUN"and Arcjet did not detect a spoofed bot.undefinedif the rule result was from a"DRY_RUN"bot rule or a non-bot rule.
Types:
type ArcjetRuleResult = { ruleId: string; fingerprint: string; ttl: number; state: ArcjetRuleState; conclusion: ArcjetConclusion; reason: ArcjetReason; isDenied: () => boolean;};is_spoofed_bot(result: RuleResult) from arcjet.
Returns True if a live bot rule detected a spoofed user agent. Returns
False for "DRY_RUN" results and non-bot rules.
isVerifiedBot / is_verified_bot
Section titled “isVerifiedBot / is_verified_bot”Determines whether a non-"DRY_RUN" bot rule detected a request from a verified
bot.
If true, the bot was verified as legitimate and you may want to ignore other
signals.
For allow rules, Arcjet verifies the authenticity of detected bots by checking
IP data and performing reverse DNS lookups. A verified bot is a bot that has
passed these checks.
isVerifiedBot(result: ArcjetRuleResult) from @arcjet/inspect.
Returns one of the following:
trueif the bot rule result was not"DRY_RUN"and Arcjet detected a verified bot.falseif the bot rule result was not"DRY_RUN"and Arcjet did not detect a verified bot.undefinedif the rule result was from a"DRY_RUN"bot rule or a non-bot rule.
Types:
type ArcjetRuleResult = { ruleId: string; fingerprint: string; ttl: number; state: ArcjetRuleState; conclusion: ArcjetConclusion; reason: ArcjetReason; isDenied: () => boolean;};is_verified_bot(result: RuleResult) from arcjet.
Returns True if a live bot rule verified the client as a known bot. Returns
False for "DRY_RUN" results and non-bot rules.
isMissingUserAgent / is_missing_user_agent
Section titled “isMissingUserAgent / is_missing_user_agent”Determines whether a non-"DRY_RUN" bot rule errored because the request was
missing a User-Agent header. If true, you may want to block the request,
because a missing User-Agent header is a good indicator of a malicious request:
RFC 9110
recommends the header.
isMissingUserAgent(result: ArcjetRuleResult) from @arcjet/inspect.
Returns one of the following:
trueif the rule result was not"DRY_RUN"and the request was missing aUser-Agentheader.falseif the rule result was not"DRY_RUN"and the request had aUser-Agentheader.undefinedif the rule result was from a"DRY_RUN"bot rule or a non-bot rule.
Types:
type ArcjetRuleResult = { ruleId: string; fingerprint: string; ttl: number; state: ArcjetRuleState; conclusion: ArcjetConclusion; reason: ArcjetReason; isDenied: () => boolean;};is_missing_user_agent(result: RuleResult) from arcjet.
Returns True if a live bot rule reported a missing User-Agent header.
Returns False for "DRY_RUN" results and non-bot rules.
What next?
Section titled “What next?”Arcjet can protect your entire app or individual routes with a few lines of code. With the main Arcjet SDK you can set up bot protection, rate limiting for your API, signup form protection to reduce fraudulent registrations, and more.