@devtune/ai-traffic 0.11.0 → 0.12.0

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/dist/express.js CHANGED
@@ -11,14 +11,18 @@ export function createDevTuneAiTrafficExpressMiddleware(options) {
11
11
  headers: createHeadersLike(request),
12
12
  url: createRequestUrl(request, origin),
13
13
  ip: request.socket?.remoteAddress ?? null,
14
+ method: request.method,
15
+ targetUri: createTargetUri(request),
14
16
  };
15
17
  client.refreshRegistryIfStale();
16
18
  if (!client.mightTrackRequest(requestLike)) {
17
19
  next();
18
20
  return;
19
21
  }
22
+ // Recorded after the response, so keep the arrival time a signature is checked against.
23
+ const receivedAt = client.signedArrivalTime(requestLike);
20
24
  response.once("finish", () => {
21
- client.trackRequest(requestLike, undefined, response.statusCode);
25
+ client.trackRequest(requestLike, undefined, response.statusCode, receivedAt);
22
26
  });
23
27
  next();
24
28
  };
@@ -28,6 +32,11 @@ function createHeadersLike(request) {
28
32
  get(name) {
29
33
  return getHeader(request, name);
30
34
  },
35
+ // Express's getter reads `request.headers` with inherited properties, so
36
+ // its result is trusted only when it is a string or an array of strings.
37
+ getCombined(name) {
38
+ return (combinedHeaderValue(request.get?.(name)) ?? ownCombinedHeaderValue(request.headers, name));
39
+ },
31
40
  };
32
41
  }
33
42
  function createRequestUrl(request, configuredOrigin) {
@@ -39,13 +48,33 @@ function createRequestUrl(request, configuredOrigin) {
39
48
  return requestPath;
40
49
  }
41
50
  }
42
- function getRequestOrigin(request) {
51
+ /**
52
+ * The URL a signature covers, as the request line and the raw host (`X-Forwarded-Host`, then `Host`) carried
53
+ * it. Never parsed, so a signed `https://EXAMPLE.com:443/guide` is forwarded
54
+ * byte for byte. A configured `origin` never replaces the host the agent
55
+ * signed. An absolute-form request target is already the whole URL. Null
56
+ * when there is no host to build it from.
57
+ */
58
+ function createTargetUri(request) {
59
+ const requestPath = request.originalUrl ?? request.url;
60
+ // An absolute-form target is already the whole URL; any other non-path
61
+ // form (`*`, authority-form) is not a URL a signature can cover.
62
+ if (!requestPath.startsWith("/")) {
63
+ return /^https?:\/\//iu.test(requestPath) ? requestPath : null;
64
+ }
65
+ const host = getRequestHost(request);
66
+ return host ? `${getRequestOrigin(request, host)}${requestPath}` : null;
67
+ }
68
+ function getRequestOrigin(request, host = getRequestHost(request) ?? "localhost") {
43
69
  const protocol = getForwardedHeader(request, "x-forwarded-proto") ?? request.protocol ?? "http";
44
- const host = getForwardedHeader(request, "x-forwarded-host") ?? getHeader(request, "host") ?? "localhost";
45
70
  return `${protocol}://${host}`;
46
71
  }
72
+ function getRequestHost(request) {
73
+ return getForwardedHeader(request, "x-forwarded-host") ?? getHeader(request, "host");
74
+ }
75
+ /** Express's getter can return a repeated field as an array; like the raw headers, it reads the first value. */
47
76
  function getHeader(request, name) {
48
- return request.get?.(name) ?? headerValue(request.headers[name.toLowerCase()]);
77
+ return headerValue(request.get?.(name)) ?? headerValue(request.headers[name.toLowerCase()]);
49
78
  }
50
79
  function getForwardedHeader(request, name) {
51
80
  const value = getHeader(request, name);
@@ -57,4 +86,27 @@ function headerValue(value) {
57
86
  }
58
87
  return value?.[0] ?? null;
59
88
  }
89
+ /**
90
+ * A covered field's value, read only as an own property: the names come from
91
+ * a client's `Signature-Input`, so `constructor` or `__proto__` must not
92
+ * reach an inherited value.
93
+ */
94
+ function ownCombinedHeaderValue(headers, name) {
95
+ const key = name.toLowerCase();
96
+ return Object.prototype.hasOwnProperty.call(headers, key)
97
+ ? combinedHeaderValue(headers[key])
98
+ : null;
99
+ }
100
+ /**
101
+ * Repeated values combine with ", " as RFC 9110 field combination does. Only
102
+ * a string or an array of strings is a value; anything else is absent.
103
+ */
104
+ function combinedHeaderValue(value) {
105
+ if (typeof value === "string") {
106
+ return value;
107
+ }
108
+ return Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string")
109
+ ? value.join(", ")
110
+ : null;
111
+ }
60
112
  //# sourceMappingURL=express.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"express.js","sourceRoot":"","sources":["../../express/src/express.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,sBAAsB,GAGvB,MAAM,qBAAqB,CAAC;AA0B7B,MAAM,UAAU,uCAAuC,CAAC,OAAuC;IAC7F,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAC;IAC7C,MAAM,MAAM,GAAG,sBAAsB,CAAC,aAAa,CAAC,CAAC;IAErD,OAAO,SAAS,iCAAiC,CAC/C,OAAuC,EACvC,QAAyC,EACzC,IAAiC;QAEjC,MAAM,WAAW,GAAG;YAClB,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC;YACnC,GAAG,EAAE,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC;YACtC,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,IAAI,IAAI;SAC1C,CAAC;QAEF,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAEhC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,IAAI,EAAE,CAAC;YAEP,OAAO;QACT,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC3B,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAuC;IAChE,OAAO;QACL,GAAG,CAAC,IAAI;YACN,OAAO,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAuC,EAAE,gBAAyB;IAC1F,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC;IAEvD,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,WAAW,EAAE,gBAAgB,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAuC;IAC/D,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,CAAC,IAAI,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC;IAChG,MAAM,IAAI,GACR,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,CAAC,IAAI,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,WAAW,CAAC;IAE/F,OAAO,GAAG,QAAQ,MAAM,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,SAAS,CAAC,OAAuC,EAAE,IAAY;IACtE,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAuC,EAAE,IAAY;IAC/E,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEvC,OAAO,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;AACjD,CAAC;AAED,SAAS,WAAW,CAAC,KAAkB;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../express/src/express.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export { DEFAULT_INGEST_ENDPOINT, DEFAULT_REGISTRY_URL, DevTuneAiTrafficClient, createDevTuneAiTraffic, createDevTuneAiTrafficMiddleware, mightBeAiBotUserAgent, withDevTuneAiTrafficRoute, type DevTuneAiTrafficEvent, type DevTuneAiTrafficLogger, type DevTuneAiTrafficMatch, type DevTuneAiTrafficOptions, type DevTuneAiTrafficRouteHandler, type HeadersLike, type RequestLike, type WaitUntilLike, } from "./client.js";
6
6
  export { SDK_VERSION, SDK_VERSION_HEADER } from "./version.js";
7
+ export { WEB_BOT_AUTH_FORWARDED_HEADERS, type DevTuneAiTrafficWebBotAuth } from "./web-bot-auth.js";
7
8
  export { detectTrustedProxy, resolveClientIp, type DevTuneAiTrafficTrustedProxy, } from "./client-ip.js";
8
9
  export { FALLBACK_AI_BOT_REGISTRY_ENTRIES, compileAiBotRegistryEntries, matchAiBotRegistryEntry, parseAiBotRegistryPayload, type AiBotRegistryEntry, type CompiledAiBotRegistryEntry, type LlmPlatform, type MachineTrafficClass, } from "./registry.js";
9
10
  export { detectAiCrawler, detectAiReferrer, FALLBACK_AI_REFERRER_REGISTRY_ENTRIES, type AiCrawlerDetection, type AiReferrerDetection, type AiReferrerRegistryEntry, type DetectAiCrawlerOptions, type DetectAiReferrerOptions, type DetectionRequestLike, } from "./detection.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,gCAAgC,EAChC,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,KAAK,4BAA4B,GAClC,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gCAAgC,EAChC,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAC/B,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,qCAAqC,EACrC,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,gCAAgC,EAChC,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EAAE,8BAA8B,EAAE,KAAK,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAEpG,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,KAAK,4BAA4B,GAClC,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gCAAgC,EAChC,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAC/B,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,qCAAqC,EACrC,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export { DEFAULT_INGEST_ENDPOINT, DEFAULT_REGISTRY_URL, DevTuneAiTrafficClient, createDevTuneAiTraffic, createDevTuneAiTrafficMiddleware, mightBeAiBotUserAgent, withDevTuneAiTrafficRoute, } from "./client.js";
6
6
  export { SDK_VERSION, SDK_VERSION_HEADER } from "./version.js";
7
+ export { WEB_BOT_AUTH_FORWARDED_HEADERS } from "./web-bot-auth.js";
7
8
  export { detectTrustedProxy, resolveClientIp, } from "./client-ip.js";
8
9
  export { FALLBACK_AI_BOT_REGISTRY_ENTRIES, compileAiBotRegistryEntries, matchAiBotRegistryEntry, parseAiBotRegistryPayload, } from "./registry.js";
9
10
  export { detectAiCrawler, detectAiReferrer, FALLBACK_AI_REFERRER_REGISTRY_ENTRIES, } from "./detection.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,gCAAgC,EAChC,qBAAqB,EACrB,yBAAyB,GAS1B,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EACL,kBAAkB,EAClB,eAAe,GAEhB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gCAAgC,EAChC,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,GAK1B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,qCAAqC,GAOtC,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,gCAAgC,EAChC,qBAAqB,EACrB,yBAAyB,GAS1B,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EAAE,8BAA8B,EAAmC,MAAM,mBAAmB,CAAC;AAEpG,OAAO,EACL,kBAAkB,EAClB,eAAe,GAEhB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gCAAgC,EAChC,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,GAK1B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,qCAAqC,GAOtC,MAAM,gBAAgB,CAAC"}
package/dist/node.d.ts CHANGED
@@ -6,6 +6,7 @@ import { type DevTuneAiTrafficOptions } from "@devtune/ai-traffic";
6
6
  type HeaderValue = string | readonly string[] | undefined;
7
7
  export interface DevTuneAiTrafficNodeRequest {
8
8
  headers: Record<string, HeaderValue>;
9
+ method?: string;
9
10
  socket?: {
10
11
  encrypted?: boolean;
11
12
  remoteAddress?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../node/src/node.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,qBAAqB,CAAC;AAE7B,KAAK,WAAW,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;AAE1D,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CACtD;AAED,MAAM,WAAW,2BAA4B,SAAQ,uBAAuB;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,2BAA2B,IAKlF,SAAS,2BAA2B,EACpC,UAAU,4BAA4B,UAkBzC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../node/src/node.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,qBAAqB,CAAC;AAE7B,KAAK,WAAW,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;AAE1D,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CACtD;AAED,MAAM,WAAW,2BAA4B,SAAQ,uBAAuB;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,2BAA2B,IAKlF,SAAS,2BAA2B,EACpC,UAAU,4BAA4B,UAuBzC"}
package/dist/node.js CHANGED
@@ -11,13 +11,17 @@ export function createDevTuneAiTrafficNodeHandler(options) {
11
11
  headers: createHeadersLike(request),
12
12
  url: createRequestUrl(request, origin),
13
13
  ip: request.socket?.remoteAddress ?? null,
14
+ method: request.method,
15
+ targetUri: createTargetUri(request),
14
16
  };
15
17
  client.refreshRegistryIfStale();
16
18
  if (!client.mightTrackRequest(requestLike)) {
17
19
  return;
18
20
  }
21
+ // Recorded after the response, so keep the arrival time a signature is checked against.
22
+ const receivedAt = client.signedArrivalTime(requestLike);
19
23
  response.once("finish", () => {
20
- client.trackRequest(requestLike, undefined, response.statusCode);
24
+ client.trackRequest(requestLike, undefined, response.statusCode, receivedAt);
21
25
  });
22
26
  };
23
27
  }
@@ -26,6 +30,9 @@ function createHeadersLike(request) {
26
30
  get(name) {
27
31
  return getHeader(request, name);
28
32
  },
33
+ getCombined(name) {
34
+ return ownCombinedHeaderValue(request.headers, name);
35
+ },
29
36
  };
30
37
  }
31
38
  function createRequestUrl(request, configuredOrigin) {
@@ -37,11 +44,30 @@ function createRequestUrl(request, configuredOrigin) {
37
44
  return requestPath;
38
45
  }
39
46
  }
40
- function getRequestOrigin(request) {
47
+ /**
48
+ * The URL a signature covers, as the request line and the raw host (`X-Forwarded-Host`, then `Host`) carried
49
+ * it. Never parsed, so a signed `https://EXAMPLE.com:443/guide` is forwarded
50
+ * byte for byte. A configured `origin` never replaces the host the agent
51
+ * signed. An absolute-form request target is already the whole URL. Null
52
+ * when there is no host to build it from.
53
+ */
54
+ function createTargetUri(request) {
55
+ const requestPath = request.url ?? "/";
56
+ // An absolute-form target is already the whole URL; any other non-path
57
+ // form (`*`, authority-form) is not a URL a signature can cover.
58
+ if (!requestPath.startsWith("/")) {
59
+ return /^https?:\/\//iu.test(requestPath) ? requestPath : null;
60
+ }
61
+ const host = getRequestHost(request);
62
+ return host ? `${getRequestOrigin(request, host)}${requestPath}` : null;
63
+ }
64
+ function getRequestOrigin(request, host = getRequestHost(request) || "localhost") {
41
65
  const forwardedProtocol = getForwardedHeader(request, "x-forwarded-proto");
42
66
  const protocol = forwardedProtocol ?? (request.socket?.encrypted ? "https" : "http");
43
- const host = getForwardedHeader(request, "x-forwarded-host") ?? getHeader(request, "host");
44
- return `${protocol}://${host || "localhost"}`;
67
+ return `${protocol}://${host}`;
68
+ }
69
+ function getRequestHost(request) {
70
+ return getForwardedHeader(request, "x-forwarded-host") ?? getHeader(request, "host");
45
71
  }
46
72
  function getHeader(request, name) {
47
73
  return headerValue(request.headers[name.toLowerCase()]);
@@ -56,4 +82,27 @@ function headerValue(value) {
56
82
  }
57
83
  return value?.[0] ?? null;
58
84
  }
85
+ /**
86
+ * A covered field's value, read only as an own property: the names come from
87
+ * a client's `Signature-Input`, so `constructor` or `__proto__` must not
88
+ * reach an inherited value.
89
+ */
90
+ function ownCombinedHeaderValue(headers, name) {
91
+ const key = name.toLowerCase();
92
+ return Object.prototype.hasOwnProperty.call(headers, key)
93
+ ? combinedHeaderValue(headers[key])
94
+ : null;
95
+ }
96
+ /**
97
+ * Repeated values combine with ", " as RFC 9110 field combination does. Only
98
+ * a string or an array of strings is a value; anything else is absent.
99
+ */
100
+ function combinedHeaderValue(value) {
101
+ if (typeof value === "string") {
102
+ return value;
103
+ }
104
+ return Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string")
105
+ ? value.join(", ")
106
+ : null;
107
+ }
59
108
  //# sourceMappingURL=node.js.map
package/dist/node.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"node.js","sourceRoot":"","sources":["../../node/src/node.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,sBAAsB,GAGvB,MAAM,qBAAqB,CAAC;AAsB7B,MAAM,UAAU,iCAAiC,CAAC,OAAoC;IACpF,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAC;IAC7C,MAAM,MAAM,GAAG,sBAAsB,CAAC,aAAa,CAAC,CAAC;IAErD,OAAO,SAAS,2BAA2B,CACzC,OAAoC,EACpC,QAAsC;QAEtC,MAAM,WAAW,GAAG;YAClB,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC;YACnC,GAAG,EAAE,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC;YACtC,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,IAAI,IAAI;SAC1C,CAAC;QAEF,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAEhC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,OAAO;QACT,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC3B,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAoC;IAC7D,OAAO;QACL,GAAG,CAAC,IAAI;YACN,OAAO,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAoC,EAAE,gBAAyB;IACvF,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC;IAEvC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,WAAW,EAAE,gBAAgB,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAoC;IAC5D,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,iBAAiB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACrF,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,CAAC,IAAI,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE3F,OAAO,GAAG,QAAQ,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;AAChD,CAAC;AAED,SAAS,SAAS,CAAC,OAAoC,EAAE,IAAY;IACnE,OAAO,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAoC,EAAE,IAAY;IAC5E,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEvC,OAAO,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;AACjD,CAAC;AAED,SAAS,WAAW,CAAC,KAAkB;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../node/src/node.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Lets DevTune tell a customer when their sensor has an upgrade available.
4
4
  */
5
5
  /** Must equal `version` in package.json; a unit test and the release check enforce it. */
6
- export declare const SDK_VERSION = "0.11.0";
6
+ export declare const SDK_VERSION = "0.12.0";
7
7
  /** Sent on every ingest and registry request. Carries the version only, no visitor data. */
8
8
  export declare const SDK_VERSION_HEADER = "x-devtune-sdk-version";
9
9
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Lets DevTune tell a customer when their sensor has an upgrade available.
4
4
  */
5
5
  /** Must equal `version` in package.json; a unit test and the release check enforce it. */
6
- export const SDK_VERSION = "0.11.0";
6
+ export const SDK_VERSION = "0.12.0";
7
7
  /** Sent on every ingest and registry request. Carries the version only, no visitor data. */
8
8
  export const SDK_VERSION_HEADER = "x-devtune-sdk-version";
9
9
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * ABOUTME: Collects the Web Bot Auth signature material DevTune needs to verify a signed agent request.
3
+ * Forwards the signature headers verbatim with the request line and covered fields; never verifies, logs or stores them.
4
+ */
5
+ import type { RequestLike } from "./client.js";
6
+ /**
7
+ * What DevTune's ingest needs to check one request's HTTP Message Signature
8
+ * (RFC 9421) as a Web Bot Auth agent signed it. The three signature headers
9
+ * are verbatim, `method` and `targetUri` are the request as received, which
10
+ * never has a query, and `headers` holds any other field the signature
11
+ * covers, by lowercase name. DevTune verifies it on receipt and never stores
12
+ * it.
13
+ */
14
+ export interface DevTuneAiTrafficWebBotAuth {
15
+ signature: string;
16
+ signatureInput: string;
17
+ signatureAgent: string;
18
+ method: string;
19
+ targetUri: string;
20
+ headers?: Record<string, string>;
21
+ }
22
+ /**
23
+ * The only header fields ever forwarded with a signature: non-sensitive
24
+ * request headers. A request whose signature covers any other field, besides
25
+ * the three signature headers, forwards no signature material at all, since
26
+ * an unknown field may carry a credential and dropping just that field would
27
+ * leave a signature that can never verify. Short enough to stay within
28
+ * ingest's limits of 16 fields and 128-character names.
29
+ */
30
+ export declare const WEB_BOT_AUTH_FORWARDED_HEADERS: readonly string[];
31
+ /**
32
+ * Reads the material for a request that carries all three of `Signature`,
33
+ * `Signature-Input` and `Signature-Agent`, or returns null.
34
+ *
35
+ * Also returns null when anything would exceed ingest's limits or is not a
36
+ * value the site could have received, rather than truncating: a truncated
37
+ * signature can never verify, and the event is then sent exactly as an
38
+ * unsigned one would be.
39
+ *
40
+ * `targetUri` must carry the authority and target the agent sent. A target
41
+ * that differs from the event's `url` only makes DevTune's check
42
+ * inconclusive, but a rewritten one that happens to match `url` turns a
43
+ * genuine signature into a failed one.
44
+ *
45
+ * A target with a query, even a bare `?`, forwards nothing. The query can
46
+ * carry a reset token, an OAuth code or search terms, and DevTune could not
47
+ * verify the signature anyway: the event's `url` never has a query, so the
48
+ * target would not match it.
49
+ */
50
+ export declare function readWebBotAuth(request: Pick<RequestLike, "headers" | "method" | "targetUri" | "url">): DevTuneAiTrafficWebBotAuth | null;
51
+ /**
52
+ * Whether a request carries all three Web Bot Auth signature headers, the
53
+ * only requests whose arrival time or signature material is worth reading.
54
+ */
55
+ export declare function hasWebBotAuthHeaders(request: Pick<RequestLike, "headers">): boolean;
56
+ //# sourceMappingURL=web-bot-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-bot-auth.d.ts","sourceRoot":"","sources":["../src/web-bot-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAwBD;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,EAAE,SAAS,MAAM,EAW3D,CAAC;AAWF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,KAAK,CAAC,GACrE,0BAA0B,GAAG,IAAI,CA+EnC;AA4CD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,WAMzE"}
@@ -0,0 +1,233 @@
1
+ /**
2
+ * ABOUTME: Collects the Web Bot Auth signature material DevTune needs to verify a signed agent request.
3
+ * Forwards the signature headers verbatim with the request line and covered fields; never verifies, logs or stores them.
4
+ */
5
+ const MAX_SIGNATURE_LENGTH = 4096;
6
+ const MAX_SIGNATURE_INPUT_LENGTH = 4096;
7
+ const MAX_SIGNATURE_AGENT_LENGTH = 2048;
8
+ const MAX_METHOD_LENGTH = 16;
9
+ const MAX_TARGET_URI_LENGTH = 8192;
10
+ const MAX_HEADER_VALUE_LENGTH = 4096;
11
+ const SIGNATURE_HEADERS = new Set(["signature", "signature-input", "signature-agent"]);
12
+ /**
13
+ * Derived components that depend on the path, query or scheme, which a Fetch
14
+ * runtime may have rewritten: it normalises the path, and behind a
15
+ * TLS-terminating proxy its `request.url` can be `http:` for an `https:`
16
+ * request.
17
+ */
18
+ const FETCH_REWRITTEN_COMPONENTS = new Set([
19
+ "@path",
20
+ "@query",
21
+ "@request-target",
22
+ "@scheme",
23
+ "@target-uri",
24
+ ]);
25
+ /**
26
+ * The only header fields ever forwarded with a signature: non-sensitive
27
+ * request headers. A request whose signature covers any other field, besides
28
+ * the three signature headers, forwards no signature material at all, since
29
+ * an unknown field may carry a credential and dropping just that field would
30
+ * leave a signature that can never verify. Short enough to stay within
31
+ * ingest's limits of 16 fields and 128-character names.
32
+ */
33
+ export const WEB_BOT_AUTH_FORWARDED_HEADERS = [
34
+ "accept",
35
+ "accept-encoding",
36
+ "accept-language",
37
+ "cache-control",
38
+ "content-length",
39
+ "content-type",
40
+ "date",
41
+ "from",
42
+ "host",
43
+ "user-agent",
44
+ ];
45
+ const FORWARDED_HEADERS = new Set(WEB_BOT_AUTH_FORWARDED_HEADERS);
46
+ /** RFC 9110 field-value characters: visible ASCII, SP and HTAB. */
47
+ const FIELD_VALUE = /^[\t\x20-\x7e]*$/u;
48
+ /** RFC 9110 method token. */
49
+ const METHOD = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/u;
50
+ /** An absolute URI as sent on the wire: visible ASCII, no whitespace. */
51
+ const TARGET_URI = /^[\x21-\x7e]+$/u;
52
+ /**
53
+ * Reads the material for a request that carries all three of `Signature`,
54
+ * `Signature-Input` and `Signature-Agent`, or returns null.
55
+ *
56
+ * Also returns null when anything would exceed ingest's limits or is not a
57
+ * value the site could have received, rather than truncating: a truncated
58
+ * signature can never verify, and the event is then sent exactly as an
59
+ * unsigned one would be.
60
+ *
61
+ * `targetUri` must carry the authority and target the agent sent. A target
62
+ * that differs from the event's `url` only makes DevTune's check
63
+ * inconclusive, but a rewritten one that happens to match `url` turns a
64
+ * genuine signature into a failed one.
65
+ *
66
+ * A target with a query, even a bare `?`, forwards nothing. The query can
67
+ * carry a reset token, an OAuth code or search terms, and DevTune could not
68
+ * verify the signature anyway: the event's `url` never has a query, so the
69
+ * target would not match it.
70
+ */
71
+ export function readWebBotAuth(request) {
72
+ if (!hasWebBotAuthHeaders(request)) {
73
+ return null;
74
+ }
75
+ const signature = request.headers.get("signature");
76
+ const signatureInput = request.headers.get("signature-input");
77
+ const signatureAgent = request.headers.get("signature-agent");
78
+ const { method } = request;
79
+ const targetUri = request.targetUri === undefined ? readFetchTargetUri(request) : request.targetUri;
80
+ if (targetUri === null ||
81
+ targetUri.includes("?") ||
82
+ !isFieldValue(signature, MAX_SIGNATURE_LENGTH) ||
83
+ !isFieldValue(signatureInput, MAX_SIGNATURE_INPUT_LENGTH) ||
84
+ !isFieldValue(signatureAgent, MAX_SIGNATURE_AGENT_LENGTH) ||
85
+ !method ||
86
+ method.length > MAX_METHOD_LENGTH ||
87
+ !METHOD.test(method) ||
88
+ targetUri.length > MAX_TARGET_URI_LENGTH ||
89
+ !TARGET_URI.test(targetUri)) {
90
+ return null;
91
+ }
92
+ const components = readCoveredComponents(signatureInput);
93
+ // A Fetch runtime has already normalised the path (`/a/../guide` arrives as
94
+ // `/guide`) and may report a proxied `https:` request as `http:`. The raw
95
+ // forms are gone, so a signature over them would be checked against a
96
+ // rewritten base and fail. Signatures over `@authority` and
97
+ // `signature-agent` alone depend on neither and still forward.
98
+ if (request.targetUri === undefined &&
99
+ components.some((component) => FETCH_REWRITTEN_COMPONENTS.has(component))) {
100
+ return null;
101
+ }
102
+ const coveredNames = components.filter((component) => !component.startsWith("@") && !SIGNATURE_HEADERS.has(component));
103
+ if (coveredNames.some((name) => !FORWARDED_HEADERS.has(name))) {
104
+ return null;
105
+ }
106
+ const headers = new Map();
107
+ for (const name of coveredNames) {
108
+ if (headers.has(name)) {
109
+ continue;
110
+ }
111
+ const value = request.headers.getCombined
112
+ ? request.headers.getCombined(name)
113
+ : request.headers.get(name);
114
+ if (value === null) {
115
+ continue;
116
+ }
117
+ if (!isFieldValue(value, MAX_HEADER_VALUE_LENGTH, true)) {
118
+ return null;
119
+ }
120
+ headers.set(name, value);
121
+ }
122
+ return {
123
+ signature,
124
+ signatureInput,
125
+ signatureAgent,
126
+ method,
127
+ targetUri,
128
+ ...(headers.size === 0 ? {} : { headers: Object.fromEntries(headers) }),
129
+ };
130
+ }
131
+ /**
132
+ * The signed target for a Fetch-compatible runtime, which hands over
133
+ * `request.url` already normalised: `https://DOCS.example.com:443/guide`
134
+ * arrives as `https://docs.example.com/guide`. The authority is put back
135
+ * from the raw header the runtime built `url` from, `X-Forwarded-Host` or
136
+ * `Host`, and falls back to the raw `Host` when neither matches, so it is
137
+ * never a rewritten one. The scheme and path still come from `url`, which is
138
+ * why a signature covering a component that depends on them is not
139
+ * forwarded from here. Null without a `Host` header, or when `url` has a
140
+ * query, which `url.search` would hide when it is a bare `?`.
141
+ */
142
+ function readFetchTargetUri(request) {
143
+ const host = request.headers.get("host");
144
+ let url;
145
+ try {
146
+ url = new URL(request.url);
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ if (!host || request.url.split("#", 1)[0]?.includes("?")) {
152
+ return null;
153
+ }
154
+ const forwardedHost = request.headers.get("x-forwarded-host")?.split(",", 1)[0]?.trim();
155
+ const authority = [forwardedHost, host].find((candidate) => candidate && normalizeHost(url.protocol, candidate) === url.host) ?? host;
156
+ return `${url.protocol}//${authority}${url.pathname}`;
157
+ }
158
+ function normalizeHost(protocol, host) {
159
+ try {
160
+ return new URL(`${protocol}//${host}`).host;
161
+ }
162
+ catch {
163
+ return null;
164
+ }
165
+ }
166
+ /**
167
+ * Whether a request carries all three Web Bot Auth signature headers, the
168
+ * only requests whose arrival time or signature material is worth reading.
169
+ */
170
+ export function hasWebBotAuthHeaders(request) {
171
+ return (request.headers.get("signature") !== null &&
172
+ request.headers.get("signature-input") !== null &&
173
+ request.headers.get("signature-agent") !== null);
174
+ }
175
+ /**
176
+ * Component identifiers a `Signature-Input` covers, across every signature it
177
+ * labels: header field names and derived components such as `@path`. This is
178
+ * just enough of RFC 8941 to find the strings that start an inner-list item:
179
+ * quoted strings are skipped whole, so a parameter value such as `key="a"` or
180
+ * `keyid="x (y)"` is never mistaken for one. Anything odd in the input is left
181
+ * for the caller or DevTune's parser to reject.
182
+ */
183
+ function readCoveredComponents(signatureInput) {
184
+ const names = [];
185
+ let inInnerList = false;
186
+ let previous = "";
187
+ for (let index = 0; index < signatureInput.length; index += 1) {
188
+ const char = signatureInput[index];
189
+ if (char === '"') {
190
+ const end = findStringEnd(signatureInput, index + 1);
191
+ if (end === -1) {
192
+ break;
193
+ }
194
+ if (inInnerList && (previous === "(" || previous === " ")) {
195
+ names.push(signatureInput.slice(index + 1, end).replace(/\\(["\\])/gu, "$1"));
196
+ }
197
+ index = end;
198
+ previous = '"';
199
+ continue;
200
+ }
201
+ if (char === "(") {
202
+ inInnerList = true;
203
+ }
204
+ else if (char === ")") {
205
+ inInnerList = false;
206
+ }
207
+ previous = char ?? "";
208
+ }
209
+ return names;
210
+ }
211
+ /** Index of the quote closing an sf-string that opened before `start`, or -1. */
212
+ function findStringEnd(value, start) {
213
+ for (let index = start; index < value.length; index += 1) {
214
+ if (value[index] === "\\") {
215
+ index += 1;
216
+ }
217
+ else if (value[index] === '"') {
218
+ return index;
219
+ }
220
+ }
221
+ return -1;
222
+ }
223
+ /**
224
+ * Also rejects a non-string, which a hand-written `HeadersLike` can return
225
+ * for a repeated field and which ingest's schema would refuse.
226
+ */
227
+ function isFieldValue(value, maxLength, allowEmpty = false) {
228
+ return (typeof value === "string" &&
229
+ (allowEmpty || value.length > 0) &&
230
+ value.length <= maxLength &&
231
+ FIELD_VALUE.test(value));
232
+ }
233
+ //# sourceMappingURL=web-bot-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-bot-auth.js","sourceRoot":"","sources":["../src/web-bot-auth.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devtune/ai-traffic",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "private": false,
5
5
  "description": "Filtered edge-push sensor for DevTune AI Traffic.",
6
6
  "license": "MIT",