Skip to content

Commit

Permalink
Add getFingerprint util
Browse files Browse the repository at this point in the history
  • Loading branch information
nandi95 committed Sep 29, 2023
1 parent 86ad8e7 commit 1db0445
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/utils/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { HTTPMethod, InferEventInput, RequestHeaders } from "../types";
import type { H3Event } from "../event";
import { validateData, ValidateFunction } from "./internal/validate";
import { getRequestWebStream } from "./body";
import crypto from "uncrypto";

export function getQuery<
T,
Expand Down Expand Up @@ -190,3 +191,22 @@ export function getRequestIP(
return event.node.req.socket.remoteAddress;
}
}

export async function getFingerprint(event: H3Event): Promise<string> {
let fingerprint = event.toString();
const ip = getRequestIP(event, { xForwardedFor: event.headers.has('x-forwarded-for') });

if (ip) {
fingerprint += `-${ip}`;
}

if (event.headers.has('user-agent')) {
fingerprint += `-${event.headers.get('user-agent')}`;
}

const buffer = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(fingerprint));

return Array.from(new Uint8Array(buffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
76 changes: 76 additions & 0 deletions test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
getRequestURL,
readFormData,
getRequestIP,
getFingerprint,
} from "../src";

describe("", () => {
Expand Down Expand Up @@ -187,6 +188,81 @@ describe("", () => {
});
});

describe("getFingerPrint", () => {
it("returns an hash", async () => {
app.use(
"/",
eventHandler(async (event) => {
return getFingerprint(event);
}),
);

const req = request.get("/");

// sha1 is 40 chars long
expect((await req).text).toHaveLength(40);
// and only uses hex chars
expect((await req).text).toMatch(/^[0-9A-Fa-f]+$/);
});

it("returns the same hash every time for same request", async () => {
app.use(
"/",
eventHandler(async (event) => {
return getFingerprint(event);
}),
);

const req = request.get("/");
expect((await req).text).toBe("41af4f039ba1d0960689b305e23d39cd458f6cb2");
expect((await req).text).toBe("41af4f039ba1d0960689b305e23d39cd458f6cb2");
});

it("uses user agent when available", async () => {
app.use(
"/",
eventHandler(async (event) => {
return getFingerprint(event);
}),
);

const req = request.get("/");
req.set("user-agent", "test");

expect((await req).text).toBe("6ae9b40f2df7f80b128ae283a6d60a3c7a81a342");
});

it("uses x-forwarded-for ip when header set", async () => {
app.use(
"/",
eventHandler(async (event) => {
return getFingerprint(event);
}),
);

const req = request.get("/");
req.set("x-forwarded-for", "something");

expect((await req).text).toBe("37d612d6f0ac50d2875b128dfa89bd9d1bcb9174");
});

it('uses the request ip when no x-forwarded-for header set', async () => {
app.use(
"/",
eventHandler(async (event) => {
return getFingerprint(event);
}),
);
app.options.onRequest = e => {
Object.defineProperty(e.node.req.socket, 'remoteAddress', { get(): any { return 'something' } });
}

const req = request.get("/");

expect((await req).text).toBe("37d612d6f0ac50d2875b128dfa89bd9d1bcb9174");
});
})

describe("assertMethod", () => {
it("only allow head and post", async () => {
app.use(
Expand Down

0 comments on commit 1db0445

Please sign in to comment.