Test Trend Micro webhooks locally
Receive Trend Micro Cloud One Conformity webhook notifications on localhost with a Horizon tunnel, and verify X-TrendMicro-Signature.
Receive Trend Micro Cloud One Conformity notifications on your laptop while you build, with a URL Conformity can reach.
This guide covers Conformity, the Trend Micro product that ngrok's guide also uses. Trend Vision One has its own webhook feature, which this page doesn't cover.
Before you begin
- Node.js 18 or later
- A Horizon account and the CLI (see Getting started)
- A reserved subdomain for
-s. Reserve one on the Subdomains page. - A Trend Micro Cloud One Conformity account
- A Next.js App Router app
Start your app
Conformity signs payloads only when you set a Webhook Security Token. The token is optional. With it, Conformity sends the X-TrendMicro-Signature header. The value is the HMAC-SHA256 (a keyed hash) hex digest of the payload body, with your token as the key.
Trend Micro's example hashes JSON.stringify of the parsed body. The handler accepts the digest of the raw body or of the re-serialized body, so it works in both cases. It compares in constant time, as Trend Micro does.
import { createHmac, timingSafeEqual } from "node:crypto";
const hexDigest = (token: string, content: string) =>
createHmac("sha256", token).update(content).digest("hex");
const matches = (received: string, expected: string) => {
const receivedBuffer = Buffer.from(received);
const expectedBuffer = Buffer.from(expected);
return (
receivedBuffer.length === expectedBuffer.length &&
timingSafeEqual(receivedBuffer, expectedBuffer)
);
};
export async function POST(request: Request) {
const token = process.env.CONFORMITY_SECURITY_TOKEN;
if (!token) {
return new Response("Missing CONFORMITY_SECURITY_TOKEN", { status: 500 });
}
const body = await request.text();
const received = request.headers.get("x-trendmicro-signature") ?? "";
let reserialized = body;
try {
reserialized = JSON.stringify(JSON.parse(body));
} catch {
return new Response("Invalid JSON", { status: 400 });
}
const isValid =
matches(received, hexDigest(token, body)) ||
matches(received, hexDigest(token, reserialized));
if (!isValid) {
return new Response("Invalid signature", { status: 401 });
}
console.log("Received Conformity notification:", JSON.parse(body));
return new Response("ok", { status: 200 });
}Store the token in an environment variable. You choose the token yourself in a later step. Conformity rejects tokens with non-base64 characters.
CONFORMITY_SECURITY_TOKEN=replace-with-your-tokenStart the app on port 3000.
npm run devStart a tunnel
Use -s with a subdomain you reserved. Without it, the subdomain is random and changes every run, so your webhook would point at a dead URL after a restart. Reserve it first on the Subdomains page. Reserved subdomains are a paid feature, see Pricing.
hrzn tunnel http://localhost:3000 -s my-appHORIZON: Tunnel connected
URL https://my-app.hrzn.run (reserved)
Forwarding http://localhost:3000
Request log https://hrzn.run/dashboard/tunnels/my-appYour public URL is https://my-app.hrzn.run. Keep this terminal open. Conformity accepts only https:// URLs, and every Horizon tunnel serves HTTPS.
Add the webhook channel in Conformity
- In Conformity, go to the Main Dashboard and select your account.
- Open Settings, then Communication settings.
- Select Update communication settings and configure Webhook.
- Select Create a Webhook channel.
- Enter
https://my-app.hrzn.run/api/webhooks/trendmicroas the Webhook URL. Conformity rejectslocalhostand IP addresses, so use the tunnel URL. - Enter the same value as
CONFORMITY_SECURITY_TOKENin Webhook Security Token. - Set automatic notifications and configure Triggers.
- Save the channel. It then appears in your Communication Settings.
Restart npm run dev if you changed .env.local after it started.
Trigger a notification
Conformity documents no test button and no resend for webhook channels. A notification arrives when one of your triggers fires. Set a broad trigger while you test, then cause that condition in your cloud account.
Check it works
When a trigger fires, your Horizon terminal prints one line:
POST 200 /api/webhooks/trendmicroYour app terminal prints Received Conformity notification: followed by the payload. If the line shows [401], see Troubleshooting.
Troubleshooting
The signature doesn't match
- Check that
CONFORMITY_SECURITY_TOKENis identical to the Webhook Security Token on the channel. - Restart
npm run devafter you edit.env.local. - If you didn't set a token, Conformity sends no
X-TrendMicro-Signatureheader and the handler rejects every request. Set a token on the channel.
Conformity rejects the webhook URL
Conformity accepts only https:// URLs. It rejects localhost and IP addresses. Use the https://my-app.hrzn.run tunnel URL.
No notification arrives
Conformity sends one only when a trigger fires. Check the triggers on the channel. Then check the Horizon request log to see whether a request reached the tunnel.
The URL changed after a restart
You started the tunnel without -s, so Horizon gave you a new random subdomain. Restart with -s my-app and the URL stays the same. -s needs a subdomain you reserved, see Pricing.
Next steps
- Read Trend Micro's Conformity webhook communication page. It also covers custom headers.
Test Signal Sciences webhooks locally
Receive Fastly Next-Gen WAF (Signal Sciences) webhook notifications on localhost with a Horizon tunnel, and verify X-SigSci-Signature.
Test Alchemy webhooks locally
Receive Alchemy Notify webhook events on localhost with a Horizon tunnel, and verify the X-Alchemy-Signature header.