@vlayer/sdk 0.1.0-nightly-20241028-74b5b0d → 0.1.0-nightly-202410292-42360cf

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.
Files changed (93) hide show
  1. package/dist/api/email/dnsResolver.d.ts +1 -0
  2. package/dist/api/email/dnsResolver.js +6 -0
  3. package/dist/api/email/dnsResolver.test.d.ts +1 -0
  4. package/dist/api/email/dnsResolver.test.js +12 -0
  5. package/dist/api/email/parseEmail.d.ts +10 -0
  6. package/dist/api/email/parseEmail.js +38 -0
  7. package/dist/api/email/parseEmail.test.d.ts +1 -0
  8. package/dist/api/email/parseEmail.test.js +91 -0
  9. package/dist/api/email/preverify.d.ts +4 -0
  10. package/dist/api/email/preverify.js +18 -0
  11. package/dist/api/email/preverify.test.d.ts +1 -0
  12. package/dist/api/email/preverify.test.js +25 -0
  13. package/dist/api/helpers.d.ts +38 -0
  14. package/dist/api/helpers.js +72 -0
  15. package/dist/api/lib/client.d.ts +6 -0
  16. package/dist/api/lib/client.js +45 -0
  17. package/dist/api/lib/types/ethereum.d.ts +14 -0
  18. package/dist/api/lib/types/ethereum.js +12 -0
  19. package/dist/api/lib/types/index.js +3 -0
  20. package/dist/api/lib/types/viem.d.ts +8 -0
  21. package/dist/api/lib/types/viem.js +1 -0
  22. package/dist/api/lib/types/vlayer.d.ts +47 -0
  23. package/dist/api/lib/types/vlayer.js +1 -0
  24. package/dist/api/lib/types/webProofProvider.d.ts +29 -0
  25. package/dist/api/lib/types/webProofProvider.js +1 -0
  26. package/dist/api/prover.d.ts +2 -0
  27. package/dist/api/prover.js +15 -0
  28. package/dist/api/v_call.d.ts +2 -0
  29. package/dist/api/v_call.js +30 -0
  30. package/dist/api/webProof/createWebProof.d.ts +2 -0
  31. package/dist/api/webProof/createWebProof.js +7 -0
  32. package/dist/api/webProof/index.js +3 -0
  33. package/dist/api/webProof/providers/extension.d.ts +2 -0
  34. package/dist/api/webProof/providers/extension.js +32 -0
  35. package/dist/api/webProof/providers/index.js +1 -0
  36. package/dist/api/webProof/steps/expectUrl.d.ts +2 -0
  37. package/dist/api/webProof/steps/expectUrl.js +8 -0
  38. package/dist/api/webProof/steps/index.d.ts +9 -0
  39. package/dist/api/webProof/steps/index.js +9 -0
  40. package/dist/api/webProof/steps/notarize.d.ts +2 -0
  41. package/dist/api/webProof/steps/notarize.js +9 -0
  42. package/dist/api/webProof/steps/startPage.d.ts +2 -0
  43. package/dist/api/webProof/steps/startPage.js +8 -0
  44. package/dist/index.d.ts +7 -0
  45. package/dist/index.js +7 -0
  46. package/dist/testHelpers/readFile.d.ts +1 -0
  47. package/dist/testHelpers/readFile.js +2 -0
  48. package/dist/web-proof-commons/index.d.ts +3 -0
  49. package/dist/web-proof-commons/index.js +3 -0
  50. package/dist/web-proof-commons/types/message.d.ts +56 -0
  51. package/dist/web-proof-commons/types/message.js +5 -0
  52. package/dist/web-proof-commons/types/webProof.d.ts +86 -0
  53. package/dist/web-proof-commons/types/webProof.js +10 -0
  54. package/dist/web-proof-commons/utils.d.ts +7 -0
  55. package/dist/web-proof-commons/utils.js +5 -0
  56. package/package.json +16 -7
  57. package/.changeset/config.json +0 -11
  58. package/CHANGELOG.md +0 -7
  59. package/eslint.config.ts +0 -22
  60. package/src/api/email/dnsResolver.test.ts +0 -18
  61. package/src/api/email/dnsResolver.ts +0 -7
  62. package/src/api/email/parseEmail.test.ts +0 -133
  63. package/src/api/email/parseEmail.ts +0 -49
  64. package/src/api/email/preverify.test.ts +0 -37
  65. package/src/api/email/preverify.ts +0 -19
  66. package/src/api/email/testdata/test_email.txt +0 -21
  67. package/src/api/email/testdata/test_email_multiple_dkims.txt +0 -28
  68. package/src/api/email/testdata/test_email_unknown_domain.txt +0 -21
  69. package/src/api/helpers.ts +0 -173
  70. package/src/api/lib/client.ts +0 -76
  71. package/src/api/lib/types/ethereum.ts +0 -43
  72. package/src/api/lib/types/viem.ts +0 -28
  73. package/src/api/lib/types/vlayer.ts +0 -60
  74. package/src/api/lib/types/webProofProvider.ts +0 -44
  75. package/src/api/prover.ts +0 -34
  76. package/src/api/v_call.ts +0 -45
  77. package/src/api/webProof/createWebProof.ts +0 -9
  78. package/src/api/webProof/providers/extension.ts +0 -72
  79. package/src/api/webProof/steps/expectUrl.ts +0 -12
  80. package/src/api/webProof/steps/index.ts +0 -11
  81. package/src/api/webProof/steps/notarize.ts +0 -17
  82. package/src/api/webProof/steps/startPage.ts +0 -12
  83. package/src/index.ts +0 -9
  84. package/src/testHelpers/readFile.ts +0 -3
  85. package/src/web-proof-commons/index.ts +0 -3
  86. package/src/web-proof-commons/types/message.ts +0 -73
  87. package/src/web-proof-commons/types/webProof.ts +0 -111
  88. package/src/web-proof-commons/utils.ts +0 -12
  89. package/tsconfig.json +0 -24
  90. package/vite.config.ts +0 -7
  91. /package/{src/api/lib/types/index.ts → dist/api/lib/types/index.d.ts} +0 -0
  92. /package/{src/api/webProof/index.ts → dist/api/webProof/index.d.ts} +0 -0
  93. /package/{src/api/webProof/providers/index.ts → dist/api/webProof/providers/index.d.ts} +0 -0
@@ -0,0 +1,8 @@
1
+ import { EXTENSION_STEP, } from "../../../web-proof-commons";
2
+ export const expectUrl = (url, label) => {
3
+ return {
4
+ url,
5
+ label,
6
+ step: EXTENSION_STEP.expectUrl,
7
+ };
8
+ };
@@ -0,0 +1,9 @@
1
+ import { expectUrl } from "./expectUrl";
2
+ import { startPage } from "./startPage";
3
+ import { notarize } from "./notarize";
4
+ declare const steps: {
5
+ expectUrl: (url: string, label: string) => import("../../../web-proof-commons").WebProofStepExpectUrl;
6
+ startPage: (url: string, label: string) => import("../../../web-proof-commons").WebProofStepStartPage;
7
+ notarize: (url: string, method: string | undefined, label: string) => import("../../../web-proof-commons").WebProofStepNotarize;
8
+ };
9
+ export { expectUrl, startPage, notarize, steps };
@@ -0,0 +1,9 @@
1
+ import { expectUrl } from "./expectUrl";
2
+ import { startPage } from "./startPage";
3
+ import { notarize } from "./notarize";
4
+ const steps = {
5
+ expectUrl,
6
+ startPage,
7
+ notarize,
8
+ };
9
+ export { expectUrl, startPage, notarize, steps };
@@ -0,0 +1,2 @@
1
+ import { WebProofStepNotarize } from "../../../web-proof-commons";
2
+ export declare const notarize: (url: string, method: string | undefined, label: string) => WebProofStepNotarize;
@@ -0,0 +1,9 @@
1
+ import { EXTENSION_STEP, } from "../../../web-proof-commons";
2
+ export const notarize = (url, method = "GET", label) => {
3
+ return {
4
+ url,
5
+ method,
6
+ label,
7
+ step: EXTENSION_STEP.notarize,
8
+ };
9
+ };
@@ -0,0 +1,2 @@
1
+ import { WebProofStepStartPage } from "../../../web-proof-commons";
2
+ export declare const startPage: (url: string, label: string) => WebProofStepStartPage;
@@ -0,0 +1,8 @@
1
+ import { EXTENSION_STEP, } from "../../../web-proof-commons";
2
+ export const startPage = (url, label) => {
3
+ return {
4
+ url,
5
+ label,
6
+ step: EXTENSION_STEP.startPage,
7
+ };
8
+ };
@@ -0,0 +1,7 @@
1
+ export * as testHelpers from "./api/helpers";
2
+ export { preverifyEmail } from "./api/email/preverify";
3
+ export { createVlayerClient } from "./api/lib/client";
4
+ export * from "./api/webProof";
5
+ export * from "./api/lib/types";
6
+ export * from "./web-proof-commons/types/webProof";
7
+ export * from "./web-proof-commons/utils";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * as testHelpers from "./api/helpers";
2
+ export { preverifyEmail } from "./api/email/preverify";
3
+ export { createVlayerClient } from "./api/lib/client";
4
+ export * from "./api/webProof";
5
+ export * from "./api/lib/types";
6
+ export * from "./web-proof-commons/types/webProof";
7
+ export * from "./web-proof-commons/utils";
@@ -0,0 +1 @@
1
+ export declare const readFile: (path: string) => string;
@@ -0,0 +1,2 @@
1
+ import fs from "fs";
2
+ export const readFile = (path) => fs.readFileSync(path).toString();
@@ -0,0 +1,3 @@
1
+ export * from "./types/message";
2
+ export * from "./types/webProof";
3
+ export * from "./utils";
@@ -0,0 +1,3 @@
1
+ export * from "./types/message";
2
+ export * from "./types/webProof";
3
+ export * from "./utils";
@@ -0,0 +1,56 @@
1
+ import type { Branded } from "../utils";
2
+ import type { WebProof } from "./webProof";
3
+ export declare const EXTENSION_STEP: {
4
+ readonly expectUrl: "expectUrl";
5
+ readonly startPage: "startPage";
6
+ readonly notarize: "notarize";
7
+ };
8
+ export type ExtensionStep = (typeof EXTENSION_STEP)[keyof typeof EXTENSION_STEP];
9
+ export declare const enum ExtensionAction {
10
+ RequestWebProof = 0
11
+ }
12
+ export type MessageToExtension = {
13
+ action: ExtensionAction;
14
+ payload: WebProverSessionConfig;
15
+ };
16
+ export declare const enum ExtensionMessageType {
17
+ ProofDone = "ProofDone",
18
+ ProofError = "ProofError",
19
+ RedirectBack = "RedirectBack",
20
+ TabOpened = "TabOpened"
21
+ }
22
+ export type ExtensionMessage = {
23
+ type: ExtensionMessageType.ProofDone;
24
+ proof: WebProof;
25
+ } | {
26
+ type: ExtensionMessageType.ProofError;
27
+ error: string;
28
+ } | {
29
+ type: ExtensionMessageType.RedirectBack;
30
+ } | {
31
+ type: ExtensionMessageType.TabOpened;
32
+ tabId: number;
33
+ };
34
+ export type WebProverSessionConfig = {
35
+ notaryUrl: string;
36
+ wsProxyUrl: string;
37
+ logoUrl: string;
38
+ steps: WebProofStep[];
39
+ };
40
+ export type WebProofStep = WebProofStepNotarize | WebProofStepExpectUrl | WebProofStepStartPage;
41
+ export type WebProofStepNotarize = Branded<{
42
+ url: string;
43
+ method: string;
44
+ label: string;
45
+ step: typeof EXTENSION_STEP.notarize;
46
+ }, "notarize">;
47
+ export type WebProofStepExpectUrl = Branded<{
48
+ url: string;
49
+ label: string;
50
+ step: typeof EXTENSION_STEP.expectUrl;
51
+ }, "expectUrl">;
52
+ export type WebProofStepStartPage = Branded<{
53
+ url: string;
54
+ label: string;
55
+ step: typeof EXTENSION_STEP.startPage;
56
+ }, "startPage">;
@@ -0,0 +1,5 @@
1
+ export const EXTENSION_STEP = {
2
+ expectUrl: "expectUrl",
3
+ startPage: "startPage",
4
+ notarize: "notarize",
5
+ };
@@ -0,0 +1,86 @@
1
+ export interface WebProof {
2
+ session: Session;
3
+ substrings: Substrings;
4
+ notaryUrl: string;
5
+ }
6
+ export interface Session {
7
+ header: Header;
8
+ signature: Signature;
9
+ session_info: SessionInfo;
10
+ }
11
+ export interface SessionInfo {
12
+ server_name: ServerName;
13
+ handshake_decommitment: HandshakeDecommitment;
14
+ }
15
+ export interface HandshakeDecommitment {
16
+ nonce: number[];
17
+ data: Data;
18
+ }
19
+ export interface Data {
20
+ server_cert_details: ServerCERTDetails;
21
+ server_kx_details: ServerKxDetails;
22
+ client_random: number[];
23
+ server_random: number[];
24
+ }
25
+ export interface ServerCERTDetails {
26
+ cert_chain: Array<number[]>;
27
+ ocsp_response: number[];
28
+ scts: null;
29
+ }
30
+ export interface ServerKxDetails {
31
+ kx_params: number[];
32
+ kx_sig: KxSig;
33
+ }
34
+ export interface KxSig {
35
+ scheme: string;
36
+ sig: number[];
37
+ }
38
+ export interface Header {
39
+ encoder_seed: number[];
40
+ merkle_root: number[];
41
+ sent_len: number;
42
+ recv_len: number;
43
+ handshake_summary: HandshakeSummary;
44
+ }
45
+ export interface HandshakeSummary {
46
+ time: number;
47
+ server_public_key: ServerPublicKey;
48
+ handshake_commitment: number[];
49
+ }
50
+ export interface ServerPublicKey {
51
+ group: string;
52
+ key: number[];
53
+ }
54
+ export interface ServerName {
55
+ Dns: string;
56
+ }
57
+ export interface Signature {
58
+ P256: string;
59
+ }
60
+ export interface Substrings {
61
+ openings: {
62
+ [key: string]: Opening[];
63
+ };
64
+ inclusion_proof: InclusionProof;
65
+ }
66
+ export interface InclusionProof {
67
+ proof: unknown[];
68
+ total_leaves: number;
69
+ }
70
+ export interface Opening {
71
+ kind?: string;
72
+ ranges?: Range[];
73
+ direction?: string;
74
+ Blake3?: Blake3;
75
+ }
76
+ export interface Blake3 {
77
+ data: number[];
78
+ nonce: number[];
79
+ }
80
+ export interface Range {
81
+ start: number;
82
+ end: number;
83
+ }
84
+ export declare const assertWebProof: (candidate: {
85
+ notaryUrl?: string;
86
+ }) => asserts candidate is WebProof;
@@ -0,0 +1,10 @@
1
+ // NOTE : this is copied from tlsn-js 5.4
2
+ // for some reason newest versions doesn't export this type (clarification is in progress)
3
+ // probably it should be reexported from tlsn-js
4
+ export const assertWebProof = function (candidate) {
5
+ //for now only thing we check is notary url
6
+ //TODO: implement later once we known the conteact with tlsn-js
7
+ if (!candidate.notaryUrl) {
8
+ throw new Error("Missing required parameter");
9
+ }
10
+ };
@@ -0,0 +1,7 @@
1
+ declare const __brand: unique symbol;
2
+ type Brand<B> = {
3
+ [__brand]: B;
4
+ };
5
+ export type Branded<T, B> = T & Brand<B>;
6
+ export declare function isDefined<T>(value: T | undefined, message?: string): asserts value is T;
7
+ export {};
@@ -0,0 +1,5 @@
1
+ export function isDefined(value, message = "Value is undefined") {
2
+ if (value === undefined) {
3
+ throw new Error(message);
4
+ }
5
+ }
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@vlayer/sdk",
3
3
  "type": "module",
4
- "exports": "./src/index.ts",
5
- "version": "0.1.0-nightly-20241028-74b5b0d",
6
- "types": "src/index.ts",
4
+ "exports": {
5
+ ".": {
6
+ "import": "./dist/index.js",
7
+ "types": "./dist/index.d.ts"
8
+ }
9
+ },
10
+ "version": "0.1.0-nightly-202410292-42360cf",
7
11
  "scripts": {
8
- "build": "npm run gen:types",
12
+ "build": "bun tsc",
9
13
  "test": "vitest --run",
10
- "gen:types": "sh ../bash/build_ts_types.sh"
14
+ "gen:types": "sh ../../bash/build_ts_types.sh"
11
15
  },
12
16
  "devDependencies": {
13
17
  "@changesets/cli": "^2.27.7",
@@ -16,7 +20,7 @@
16
20
  "vitest": "^2.1.1"
17
21
  },
18
22
  "peerDependencies": {
19
- "typescript": "^5.0.0"
23
+ "typescript": "^5.6.3"
20
24
  },
21
25
  "dependencies": {
22
26
  "dns-over-http-resolver": "^3.0.3",
@@ -24,5 +28,10 @@
24
28
  "viem": "2.21.0",
25
29
  "vite-tsconfig-paths": "^5.0.1",
26
30
  "@vitejs/plugin-react": "^4.3.2"
27
- }
31
+ },
32
+ "files": [
33
+ "dist/",
34
+ "package.json",
35
+ "README.md"
36
+ ]
28
37
  }
@@ -1,11 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json",
3
- "changelog": "@changesets/cli/changelog",
4
- "commit": false,
5
- "fixed": [],
6
- "linked": [],
7
- "access": "restricted",
8
- "baseBranch": "main",
9
- "updateInternalDependencies": "patch",
10
- "ignore": []
11
- }
package/CHANGELOG.md DELETED
@@ -1,7 +0,0 @@
1
- # @vlayer/sdk
2
-
3
- ## 0.0.2
4
-
5
- ### Patch Changes
6
-
7
- - added changesets
package/eslint.config.ts DELETED
@@ -1,22 +0,0 @@
1
- import js from "@eslint/js";
2
- import ts from "typescript-eslint";
3
- import prettierRecommended from "eslint-plugin-prettier/recommended";
4
- import globals from "globals";
5
-
6
- export default [
7
- js.configs.recommended,
8
- ...ts.configs.recommended,
9
- prettierRecommended,
10
- {
11
- rules: {
12
- "no-unused-vars": "warn",
13
- "no-undef": "warn",
14
- },
15
- languageOptions: {
16
- globals: {
17
- ...globals.browser,
18
- Bun: false,
19
- },
20
- },
21
- },
22
- ];
@@ -1,18 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { resolveDkimDns } from "./dnsResolver.ts";
3
-
4
- describe("resolveDkimDns Integration", () => {
5
- test("resolves VLayer DNS", async () => {
6
- const resolved = await resolveDkimDns(
7
- "vlayer-xyz.20230601.gappssmtp.com",
8
- "20230601",
9
- );
10
- const expected =
11
- "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3gWcOhCm99qzN+h7/2+LeP3CLsJkQQ4EP/2mrceXle5pKq8uZmBl1U4d2Vxn4w+pWFANDLmcHolLboESLFqEL5N6ae7u9b236dW4zn9AFkXAGenTzQEeif9VUFtLAZ0Qh2eV7OQgz/vPj5IaNqJ7h9hpM9gO031fe4v+J0DLCE8Rgo7hXbNgJavctc0983DaCDQaznHZ44LZ6TtZv9TBs+QFvsy4+UCTfsuOtHzoEqOOuXsVXZKLP6B882XbEnBpXEF8QzV4J26HiAJFUbO3mAqZL2UeKC0hhzoIZqZXNG0BfuzOF0VLpDa18GYMUiu+LhEJPJO9D8zhzvQIHNrpGwIDAQAB";
12
- expect(resolved).toBe(expected);
13
- });
14
-
15
- test("throws error if dns not found", async () => {
16
- await expect(resolveDkimDns("not-a-domain.com", "abcd")).rejects.toThrow();
17
- });
18
- });
@@ -1,7 +0,0 @@
1
- import DnsResolver from "dns-over-http-resolver";
2
-
3
- export async function resolveDkimDns(domain: string, selector: string) {
4
- const resolver = new DnsResolver();
5
- const address = await resolver.resolveTxt(`${selector}._domainkey.${domain}`);
6
- return address.flat()[0];
7
- }
@@ -1,133 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { getDkimSigners, parseEmail, parseParams } from "./parseEmail.ts";
3
-
4
- const emailHeaders = `From: "John Doe" <john@d.oe>
5
- To: "Jane Doe" <jane@d.oe>
6
- Subject: Hello World
7
- Date: Thu, 1 Jan 1970 00:00:00 +0000
8
- `;
9
-
10
- const dkimHeader =
11
- "DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; h=from:to:subject; s=selector1; b=abcdef;";
12
-
13
- const body = "Hello, World!";
14
-
15
- const emailFixture = `${emailHeaders}${dkimHeader}\n\n${body}`;
16
-
17
- describe("parseEmail", () => {
18
- test("should get dkim header from email", async () => {});
19
-
20
- test("correctly parses untrimmed email", async () => {
21
- const untrimmed = `\n ${emailFixture} \n`;
22
- const email = await parseEmail(untrimmed);
23
- expect(email.headers.find((h) => h.key === "dkim-signature")).toBeDefined();
24
- });
25
-
26
- test("works well with multiple dkim headers", async () => {
27
- const dkimHeader2 =
28
- "DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=second.signer; h=from:to:subject; s=selector2; b=abcdef;";
29
-
30
- const email = await parseEmail(
31
- `${emailHeaders}${dkimHeader}\n${dkimHeader2}\n\n${body}`,
32
- );
33
- const dkim = email.headers.filter((h) => h.key === "dkim-signature");
34
-
35
- expect(dkim).toHaveLength(2);
36
- expect(parseParams(dkim[0].value).s).toBe("selector1");
37
- expect(parseParams(dkim[1].value).s).toBe("selector2");
38
- });
39
- });
40
-
41
- describe("getDkimSigners", () => {
42
- test("should get dkim signers from email", async () => {
43
- const email = await parseEmail(emailFixture);
44
- const dkim = getDkimSigners(email);
45
- expect(dkim).toHaveLength(1);
46
- expect(dkim[0].domain).toBe("example.com");
47
- expect(dkim[0].selector).toBe("selector1");
48
- });
49
-
50
- test("should get multiple dkim signers from email", async () => {
51
- const dkimHeader2 =
52
- "DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=second.signer; h=from:to:subject; s=selector2; b=abcdef;";
53
- const email = await parseEmail(
54
- `${emailHeaders}${dkimHeader}\n${dkimHeader2}\n\n${body}`,
55
- );
56
-
57
- const dkim = getDkimSigners(email);
58
- expect(dkim).toHaveLength(2);
59
- expect(dkim[0].domain).toBe("example.com");
60
- expect(dkim[0].selector).toBe("selector1");
61
- expect(dkim[1].domain).toBe("second.signer");
62
- expect(dkim[1].selector).toBe("selector2");
63
- });
64
-
65
- test("should throw if no dkim header found", async () => {
66
- const email = await parseEmail(emailHeaders);
67
- expect(() => getDkimSigners(email)).toThrowError("No DKIM header found");
68
- });
69
-
70
- test("should throw if dkim header is invalid", async () => {
71
- const email = await parseEmail(
72
- `${emailHeaders}DKIM-Signature: invalid\n\n${body}`,
73
- );
74
- expect(() => getDkimSigners(email)).toThrowError(
75
- "DKIM header missing domain",
76
- );
77
- });
78
-
79
- test("should throw if dkim header is missing domain", async () => {
80
- const email = await parseEmail(
81
- `${emailHeaders}DKIM-Signature: v=1; s=selector\n\n${body}`,
82
- );
83
- expect(() => getDkimSigners(email)).toThrowError(
84
- "DKIM header missing domain",
85
- );
86
- });
87
-
88
- test("should throw if dkim header is missing selector", async () => {
89
- const email = await parseEmail(
90
- `${emailHeaders}DKIM-Signature: v=1; d=example.com\n\n${body}`,
91
- );
92
- expect(() => getDkimSigners(email)).toThrowError(
93
- "DKIM header missing selector",
94
- );
95
- });
96
- });
97
-
98
- describe("parseParams", () => {
99
- test("should parse single parameter", () => {
100
- const params = parseParams("a=b");
101
- expect(params).toEqual({ a: "b" });
102
- });
103
-
104
- test("should parse multiple parameters", () => {
105
- const params = parseParams("a=b; c=d; e=f");
106
- expect(params).toEqual({ a: "b", c: "d", e: "f" });
107
- });
108
-
109
- test("should trim spaces around parameters", () => {
110
- const params = parseParams(" a = b ; c = d ; e = f ");
111
- expect(params).toEqual({ a: "b", c: "d", e: "f" });
112
- });
113
-
114
- test("should handle empty values", () => {
115
- const params = parseParams("a=; b=c");
116
- expect(params).toEqual({ a: "", b: "c" });
117
- });
118
-
119
- test("should handle missing values", () => {
120
- const params = parseParams("a; b=c");
121
- expect(params).toEqual({ a: undefined, b: "c" });
122
- });
123
-
124
- test("should handle empty string", () => {
125
- const params = parseParams("");
126
- expect(params).toEqual({});
127
- });
128
-
129
- test("should handle parameters with extra semicolons", () => {
130
- const params = parseParams("a=b;; c=d;");
131
- expect(params).toEqual({ a: "b", c: "d" });
132
- });
133
- });
@@ -1,49 +0,0 @@
1
- import PostalMime, { Email, Header } from "postal-mime";
2
-
3
- export class DkimParsingError extends Error {
4
- constructor(message: string) {
5
- super(message);
6
- this.name = "DkimParsingError";
7
- }
8
- }
9
-
10
- export async function parseEmail(mime: string) {
11
- return await PostalMime.parse(mime.trim());
12
- }
13
-
14
- export function getDkimSigners(mail: Email) {
15
- const dkimHeader = mail.headers.filter((h) => h.key === "dkim-signature");
16
- if (dkimHeader.length === 0)
17
- throw new DkimParsingError("No DKIM header found");
18
- return dkimHeader.map(parseHeader);
19
- }
20
-
21
- export function parseParams(str: string) {
22
- return Object.fromEntries(
23
- str.split(";").map((s) =>
24
- s
25
- .trim()
26
- .split("=")
27
- .map((v) => v && v.trim()),
28
- ),
29
- ) as Record<string, string>;
30
- }
31
-
32
- function parseHeader(header: Header) {
33
- const params = parseParams(header.value);
34
- if (!params) {
35
- throw new DkimParsingError(`Invalid DKIM header ${header.value}`);
36
- }
37
-
38
- if (!params.d) {
39
- throw new DkimParsingError("DKIM header missing domain");
40
- }
41
-
42
- if (!params.s) {
43
- throw new DkimParsingError("DKIM header missing selector");
44
- }
45
- return {
46
- domain: params.d,
47
- selector: params.s,
48
- };
49
- }
@@ -1,37 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { preverifyEmail } from "./preverify.ts";
3
- import { readFile } from "../../testHelpers/readFile";
4
-
5
- describe("Preverify email: integration", () => {
6
- test("adds dns record to email mime", async () => {
7
- const rawEmail = readFile("./src/api/email/testdata/test_email.txt");
8
- const preverifiedEmail = await preverifyEmail(rawEmail);
9
- expect(preverifiedEmail).toMatchObject({
10
- email: rawEmail,
11
- dnsRecords: [expect.stringContaining("v=DKIM1; k=rsa; p=")],
12
- });
13
- });
14
-
15
- test("throws error if DKIM not found", async () => {
16
- const emailWithNoDkimHeader = 'From: "Alice"\n\nBody';
17
- await expect(preverifyEmail(emailWithNoDkimHeader)).rejects.toThrow(
18
- "No DKIM header found",
19
- );
20
- });
21
-
22
- test("throws error if DNS could not be resolved", async () => {
23
- const emailWithNoDkimHeader = readFile(
24
- "./src/api/email/testdata/test_email_unknown_domain.txt",
25
- );
26
- await expect(preverifyEmail(emailWithNoDkimHeader)).rejects.toThrow();
27
- });
28
-
29
- test("throws error if multiple DNS records found", async () => {
30
- const emailWithNoDkimHeader = readFile(
31
- "./src/api/email/testdata/test_email_multiple_dkims.txt",
32
- );
33
- await expect(preverifyEmail(emailWithNoDkimHeader)).rejects.toThrow(
34
- "Multiple DKIM headers found",
35
- );
36
- });
37
- });
@@ -1,19 +0,0 @@
1
- import { parseEmail, getDkimSigners } from "./parseEmail.ts";
2
- import { resolveDkimDns } from "./dnsResolver.ts";
3
-
4
- export async function preverifyEmail(mimeEmail: string) {
5
- const parsedEmail = await parseEmail(mimeEmail);
6
- const signers = getDkimSigners(parsedEmail);
7
- if (signers.length === 0) {
8
- throw new Error("No DKIM header found");
9
- }
10
- if (signers.length > 1) {
11
- throw new Error("Multiple DKIM headers found");
12
- }
13
- const [{ domain, selector }] = signers;
14
- const dnsRecord = await resolveDkimDns(domain, selector);
15
- return {
16
- email: mimeEmail,
17
- dnsRecords: [dnsRecord],
18
- };
19
- }
@@ -1,21 +0,0 @@
1
- DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
2
- c=simple/simple; d=google.com;
3
- h=Received:From:To:Subject:Date:Message-ID; i=joe@football.example.com;
4
- s=20230601; t=1615825284; v=1;
5
- b=Xh4Ujb2wv5x54gXtulCiy4C0e+plRm6pZ4owF+kICpYzs/8WkTVIDBrzhJP0DAYCpnL62T0G
6
- k+0OH8pi/yqETVjKtKk+peMnNvKkut0GeWZMTze0bfq3/JUK3Ln3jTzzpXxrgVnvBxeY9EZIL4g
7
- s4wwFRRKz/1bksZGSjD8uuSU=
8
- Received: from client1.football.example.com [192.0.2.1]
9
- by submitserver.example.com with SUBMISSION;
10
- Fri, 11 Jul 2003 21:01:54 -0700 (PDT)
11
- From: Joe SixPack <joe@football.example.com>
12
- To: Suzie Q <suzie@shopping.example.net>
13
- Subject: Is dinner ready?
14
- Date: Fri, 11 Jul 2003 21:00:37 -0700 (PDT)
15
- Message-ID: <20030712040037.46341.5F8J@football.example.com>
16
-
17
- Hi.
18
-
19
- We lost the game. Are you hungry yet?
20
-
21
- Joe.