Skip to content

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.

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.

Terminal window
npm install -S @arcjet/inspect
// 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" });
}

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:

  • true if the bot rule result was not "DRY_RUN" and Arcjet detected a spoofed bot.
  • false if the bot rule result was not "DRY_RUN" and Arcjet did not detect a spoofed bot.
  • undefined if 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;
};

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:

  • true if the bot rule result was not "DRY_RUN" and Arcjet detected a verified bot.
  • false if the bot rule result was not "DRY_RUN" and Arcjet did not detect a verified bot.
  • undefined if 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;
};

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:

  • true if the rule result was not "DRY_RUN" and the request was missing a User-Agent header.
  • false if the rule result was not "DRY_RUN" and the request had a User-Agent header.
  • undefined if 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;
};

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.

Discussion