@vlayer/sdk 0.1.0-nightly-20241028-591419e → 0.1.0-nightly-202410292-42360cf

Sign up to get free protection for your applications and to get access to all the features.
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-591419e",
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.