@vlayer/sdk 0.1.0-nightly-20241001-aa0406f → 0.1.0-nightly-20241001-f254bee
Sign up to get free protection for your applications and to get access to all the features.
package/package.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"name": "@vlayer/sdk",
|
3
3
|
"type": "module",
|
4
4
|
"module": "src/index.ts",
|
5
|
-
"version": "0.1.0-nightly-20241001-
|
5
|
+
"version": "0.1.0-nightly-20241001-f254bee",
|
6
6
|
"scripts": {
|
7
7
|
"build": "npm run gen:types",
|
8
8
|
"test": "vitest --run",
|
@@ -19,7 +19,9 @@
|
|
19
19
|
"typescript": "^5.0.0"
|
20
20
|
},
|
21
21
|
"dependencies": {
|
22
|
+
"dns-over-http-resolver": "^3.0.3",
|
22
23
|
"mailparser": "^3.7.1",
|
24
|
+
"postal-mime": "^2.3.2",
|
23
25
|
"viem": "^2.21.0"
|
24
26
|
}
|
25
27
|
}
|
@@ -1,14 +1,7 @@
|
|
1
|
-
import
|
1
|
+
import DnsResolver from "dns-over-http-resolver";
|
2
2
|
|
3
3
|
export async function resolveDkimDns(domain: string, selector: string) {
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
reject(err);
|
8
|
-
return;
|
9
|
-
}
|
10
|
-
|
11
|
-
resolve(addresses.flat()[0]);
|
12
|
-
});
|
13
|
-
});
|
4
|
+
const resolver = new DnsResolver();
|
5
|
+
const address = await resolver.resolveTxt(`${selector}._domainkey.${domain}`);
|
6
|
+
return address.flat()[0];
|
14
7
|
}
|
@@ -1,7 +1,5 @@
|
|
1
|
-
import assert from "node:assert";
|
2
1
|
import { describe, expect, test } from "vitest";
|
3
|
-
import { getDkimSigners, parseEmail } from "./parseEmail.ts";
|
4
|
-
import { StructuredHeader } from "mailparser";
|
2
|
+
import { getDkimSigners, parseEmail, parseParams } from "./parseEmail.ts";
|
5
3
|
|
6
4
|
const emailHeaders = `From: "John Doe" <john@d.oe>
|
7
5
|
To: "Jane Doe" <jane@d.oe>
|
@@ -17,18 +15,12 @@ const body = "Hello, World!";
|
|
17
15
|
const emailFixture = `${emailHeaders}${dkimHeader}\n\n${body}`;
|
18
16
|
|
19
17
|
describe("parseEmail", () => {
|
20
|
-
test("should get dkim header from email", async () => {
|
21
|
-
const email = await parseEmail(emailFixture);
|
22
|
-
const dkim = email.headers.get("dkim-signature")!;
|
23
|
-
assert(typeof dkim === "object" && "params" in dkim);
|
24
|
-
expect(dkim.params.d).toBe("example.com");
|
25
|
-
expect(dkim.params.s).toBe("selector1");
|
26
|
-
});
|
18
|
+
test("should get dkim header from email", async () => {});
|
27
19
|
|
28
20
|
test("correctly parses untrimmed email", async () => {
|
29
21
|
const untrimmed = `\n ${emailFixture} \n`;
|
30
22
|
const email = await parseEmail(untrimmed);
|
31
|
-
expect(email.headers.
|
23
|
+
expect(email.headers.find((h) => h.key === "dkim-signature")).toBeDefined();
|
32
24
|
});
|
33
25
|
|
34
26
|
test("works well with multiple dkim headers", async () => {
|
@@ -38,12 +30,11 @@ describe("parseEmail", () => {
|
|
38
30
|
const email = await parseEmail(
|
39
31
|
`${emailHeaders}${dkimHeader}\n${dkimHeader2}\n\n${body}`,
|
40
32
|
);
|
41
|
-
const dkim = email.headers.
|
42
|
-
|
43
|
-
)! as unknown as StructuredHeader[];
|
33
|
+
const dkim = email.headers.filter((h) => h.key === "dkim-signature")!;
|
34
|
+
|
44
35
|
expect(dkim).toHaveLength(2);
|
45
|
-
expect(dkim[0].
|
46
|
-
expect(dkim[1].
|
36
|
+
expect(parseParams(dkim[0].value).s).toBe("selector1");
|
37
|
+
expect(parseParams(dkim[1].value).s).toBe("selector2");
|
47
38
|
});
|
48
39
|
});
|
49
40
|
|
@@ -103,3 +94,40 @@ describe("getDkimSigners", () => {
|
|
103
94
|
);
|
104
95
|
});
|
105
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,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import PostalMime, { Email, Header } from "postal-mime";
|
2
2
|
|
3
3
|
export class DkimParsingError extends Error {
|
4
4
|
constructor(message: string) {
|
@@ -8,38 +8,42 @@ export class DkimParsingError extends Error {
|
|
8
8
|
}
|
9
9
|
|
10
10
|
export async function parseEmail(mime: string) {
|
11
|
-
return
|
12
|
-
skipHtmlToText: true,
|
13
|
-
skipTextToHtml: true,
|
14
|
-
skipTextLinks: true,
|
15
|
-
skipImageLinks: true,
|
16
|
-
});
|
11
|
+
return await PostalMime.parse(mime.trim());
|
17
12
|
}
|
18
13
|
|
19
|
-
export function getDkimSigners(mail:
|
20
|
-
const dkimHeader = mail.headers.
|
21
|
-
if (
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
+
);
|
26
30
|
}
|
27
31
|
|
28
|
-
function parseHeader(header:
|
29
|
-
|
32
|
+
function parseHeader(header: Header) {
|
33
|
+
const params = parseParams(header.value);
|
34
|
+
if (!params) {
|
30
35
|
throw new DkimParsingError(`Invalid DKIM header ${header}`);
|
31
36
|
}
|
32
37
|
|
33
|
-
if (!
|
38
|
+
if (!params.d) {
|
34
39
|
throw new DkimParsingError("DKIM header missing domain");
|
35
40
|
}
|
36
41
|
|
37
|
-
if (!
|
42
|
+
if (!params.s) {
|
38
43
|
throw new DkimParsingError("DKIM header missing selector");
|
39
44
|
}
|
40
|
-
|
41
45
|
return {
|
42
|
-
domain:
|
43
|
-
selector:
|
46
|
+
domain: params.d,
|
47
|
+
selector: params.s,
|
44
48
|
};
|
45
49
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { describe, expect, test } from "vitest";
|
2
2
|
import { preverifyEmail } from "./preverify.ts";
|
3
|
-
import { readFile } from "testHelpers/readFile";
|
3
|
+
import { readFile } from "../../testHelpers/readFile";
|
4
4
|
|
5
5
|
describe("Preverify email: integration", () => {
|
6
6
|
test("adds dns record to email mime", async () => {
|