@haibun/email-testing 1.13.6 → 1.40.3
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/build/email-testing-stepper-env.test.d.ts +1 -0
- package/build/email-testing-stepper-env.test.js +45 -0
- package/build/email-testing-stepper-env.test.js.map +1 -0
- package/build/email-testing-stepper-imap.test.d.ts +1 -0
- package/build/email-testing-stepper-imap.test.js +45 -0
- package/build/email-testing-stepper-imap.test.js.map +1 -0
- package/build/email-testing-stepper.d.ts +74 -0
- package/build/email-testing-stepper.js +101 -113
- package/build/email-testing-stepper.js.map +1 -0
- package/build/lib/dmarc.d.ts +2 -0
- package/build/lib/dmarc.js +25 -0
- package/build/lib/dmarc.js.map +1 -0
- package/build/lib/imap.d.ts +3 -0
- package/build/lib/imap.js +99 -0
- package/build/lib/imap.js.map +1 -0
- package/build/lib/mailauth.d.ts +2 -0
- package/build/lib/mailauth.js +33 -0
- package/build/lib/mailauth.js.map +1 -0
- package/build/lib/mta.d.ts +2 -0
- package/build/lib/mta.js +3 -0
- package/build/lib/mta.js.map +1 -0
- package/package.json +21 -44
- package/build/email-testing-stepper.test.js +0 -35
- package/build/email-testingstepper.js +0 -97
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { testWithDefaults } from '@haibun/core/build/lib/test/lib.js';
|
|
3
|
+
import { asFeatures } from '@haibun/core/build/lib/resolver-features.js';
|
|
4
|
+
import { getStepperOptionName } from '@haibun/core/build/lib/util/index.js';
|
|
5
|
+
import { DEF_PROTO_OPTIONS } from '@haibun/core/build/lib/run.js';
|
|
6
|
+
import EmailTestingStepper from './email-testing-stepper.js';
|
|
7
|
+
const domain = 'google.com';
|
|
8
|
+
const protoOptions = {
|
|
9
|
+
...DEF_PROTO_OPTIONS,
|
|
10
|
+
extraOptions: {
|
|
11
|
+
[getStepperOptionName(EmailTestingStepper, 'EMAIL_SERVER')]: domain,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
describe.skip('MTA', () => {
|
|
15
|
+
it('MTA STS', async () => {
|
|
16
|
+
const features = asFeatures([
|
|
17
|
+
{
|
|
18
|
+
path: '/features/record.feature',
|
|
19
|
+
content: `domain's mail server agent strict transfer security is valid`,
|
|
20
|
+
},
|
|
21
|
+
]);
|
|
22
|
+
const { ok } = await testWithDefaults(features, [EmailTestingStepper], protoOptions);
|
|
23
|
+
expect(ok).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('DMARC', () => {
|
|
27
|
+
it.skip('Has a valid dmarc record', async () => {
|
|
28
|
+
const features = asFeatures([
|
|
29
|
+
{ path: '/features/record.feature', content: `DMARC record exists` },
|
|
30
|
+
]);
|
|
31
|
+
const { ok } = await testWithDefaults(features, [EmailTestingStepper], protoOptions);
|
|
32
|
+
expect(ok).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
it('DMARC rua is defined', async () => {
|
|
35
|
+
const features = asFeatures([
|
|
36
|
+
{
|
|
37
|
+
path: '/features/record.feature',
|
|
38
|
+
content: `DMARC field rua is defined`,
|
|
39
|
+
},
|
|
40
|
+
]);
|
|
41
|
+
const { ok } = await testWithDefaults(features, [EmailTestingStepper], protoOptions);
|
|
42
|
+
expect(ok).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=email-testing-stepper-env.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-testing-stepper-env.test.js","sourceRoot":"","sources":["../src/email-testing-stepper-env.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,6CAA6C,CAAC;AAEzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,mBAAmB,MAAM,4BAA4B,CAAC;AAE7D,MAAM,MAAM,GAAG,YAAY,CAAC;AAE5B,MAAM,YAAY,GAAG;IACnB,GAAG,iBAAiB;IACpB,YAAY,EAAE;QACZ,CAAC,oBAAoB,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC,EAAE,MAAM;KACpE;CACF,CAAC;AACF,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QACvB,MAAM,QAAQ,GAAG,UAAU,CAAC;YAC1B;gBACE,IAAI,EAAE,0BAA0B;gBAChC,OAAO,EAAE,8DAA8D;aACxE;SACF,CAAC,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,gBAAgB,CACnC,QAAQ,EACR,CAAC,mBAAmB,CAAC,EACrB,YAAY,CACb,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,IAAI,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,QAAQ,GAAG,UAAU,CAAC;YAC1B,EAAE,IAAI,EAAE,0BAA0B,EAAE,OAAO,EAAE,qBAAqB,EAAE;SACrE,CAAC,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,gBAAgB,CACnC,QAAQ,EACR,CAAC,mBAAmB,CAAC,EACrB,YAAY,CACb,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QACpC,MAAM,QAAQ,GAAG,UAAU,CAAC;YAC1B;gBACE,IAAI,EAAE,0BAA0B;gBAChC,OAAO,EAAE,4BAA4B;aACtC;SACF,CAAC,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,gBAAgB,CACnC,QAAQ,EACR,CAAC,mBAAmB,CAAC,EACrB,YAAY,CACb,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { testWithDefaults } from '@haibun/core/build/lib/test/lib.js';
|
|
3
|
+
import { asFeatures } from '@haibun/core/build/lib/resolver-features.js';
|
|
4
|
+
import { getStepperOptionName } from '@haibun/core/build/lib/util/index.js';
|
|
5
|
+
import { DEF_PROTO_OPTIONS } from '@haibun/core/build/lib/run.js';
|
|
6
|
+
import EmailTestingStepper from './email-testing-stepper.js';
|
|
7
|
+
const domain = 'google.com';
|
|
8
|
+
const protoOptions = {
|
|
9
|
+
...DEF_PROTO_OPTIONS,
|
|
10
|
+
extraOptions: {
|
|
11
|
+
[getStepperOptionName(EmailTestingStepper, 'EMAIL_SERVER')]: domain,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
describe.skip('MTA', () => {
|
|
15
|
+
it('MTA STS', async () => {
|
|
16
|
+
const features = asFeatures([
|
|
17
|
+
{
|
|
18
|
+
path: '/features/record.feature',
|
|
19
|
+
content: `domain's mail server agent strict transfer security is valid`,
|
|
20
|
+
},
|
|
21
|
+
]);
|
|
22
|
+
const { ok } = await testWithDefaults(features, [EmailTestingStepper], protoOptions);
|
|
23
|
+
expect(ok).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('DMARC', () => {
|
|
27
|
+
it.skip('Has a valid dmarc record', async () => {
|
|
28
|
+
const features = asFeatures([
|
|
29
|
+
{ path: '/features/record.feature', content: `DMARC record exists` },
|
|
30
|
+
]);
|
|
31
|
+
const { ok } = await testWithDefaults(features, [EmailTestingStepper], protoOptions);
|
|
32
|
+
expect(ok).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
it('DMARC rua is defined', async () => {
|
|
35
|
+
const features = asFeatures([
|
|
36
|
+
{
|
|
37
|
+
path: '/features/record.feature',
|
|
38
|
+
content: `DMARC field rua is defined`,
|
|
39
|
+
},
|
|
40
|
+
]);
|
|
41
|
+
const { ok } = await testWithDefaults(features, [EmailTestingStepper], protoOptions);
|
|
42
|
+
expect(ok).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=email-testing-stepper-imap.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-testing-stepper-imap.test.js","sourceRoot":"","sources":["../src/email-testing-stepper-imap.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,6CAA6C,CAAC;AAEzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,mBAAmB,MAAM,4BAA4B,CAAC;AAE7D,MAAM,MAAM,GAAG,YAAY,CAAC;AAE5B,MAAM,YAAY,GAAG;IACnB,GAAG,iBAAiB;IACpB,YAAY,EAAE;QACZ,CAAC,oBAAoB,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC,EAAE,MAAM;KACpE;CACF,CAAC;AACF,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QACvB,MAAM,QAAQ,GAAG,UAAU,CAAC;YAC1B;gBACE,IAAI,EAAE,0BAA0B;gBAChC,OAAO,EAAE,8DAA8D;aACxE;SACF,CAAC,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,gBAAgB,CACnC,QAAQ,EACR,CAAC,mBAAmB,CAAC,EACrB,YAAY,CACb,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,IAAI,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,QAAQ,GAAG,UAAU,CAAC;YAC1B,EAAE,IAAI,EAAE,0BAA0B,EAAE,OAAO,EAAE,qBAAqB,EAAE;SACrE,CAAC,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,gBAAgB,CACnC,QAAQ,EACR,CAAC,mBAAmB,CAAC,EACrB,YAAY,CACb,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QACpC,MAAM,QAAQ,GAAG,UAAU,CAAC;YAC1B;gBACE,IAAI,EAAE,0BAA0B;gBAChC,OAAO,EAAE,4BAA4B;aACtC;SACF,CAAC,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,gBAAgB,CACnC,QAAQ,EACR,CAAC,mBAAmB,CAAC,EACrB,YAAY,CACb,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { AStepper, TNamed, TWorld } from '@haibun/core/build/lib/defs.js';
|
|
2
|
+
import 'dotenv/config';
|
|
3
|
+
declare const EmailTestingStepper: {
|
|
4
|
+
new (): {
|
|
5
|
+
knownPolicy: undefined;
|
|
6
|
+
emailServer: undefined | string;
|
|
7
|
+
options: {
|
|
8
|
+
EMAIL_SERVER: {
|
|
9
|
+
desc: string;
|
|
10
|
+
parse: (input: string) => {
|
|
11
|
+
error: string;
|
|
12
|
+
result?: undefined;
|
|
13
|
+
} | {
|
|
14
|
+
result: string;
|
|
15
|
+
error?: undefined;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
setWorld(world: TWorld, steppers: AStepper[]): Promise<void>;
|
|
20
|
+
getPolicy(): Promise<any>;
|
|
21
|
+
steps: {
|
|
22
|
+
dmarcExists: {
|
|
23
|
+
gwta: string;
|
|
24
|
+
action: () => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
25
|
+
};
|
|
26
|
+
dmarcExistsFor: {
|
|
27
|
+
gwta: string;
|
|
28
|
+
action: ({ domain }: TNamed) => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
29
|
+
};
|
|
30
|
+
dmarcHas: {
|
|
31
|
+
gwta: string;
|
|
32
|
+
action: ({ field }: TNamed) => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
33
|
+
};
|
|
34
|
+
dmarcHasFor: {
|
|
35
|
+
gwta: string;
|
|
36
|
+
action: ({ domain, field }: TNamed) => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
37
|
+
};
|
|
38
|
+
checkMTASTS: {
|
|
39
|
+
gwta: string;
|
|
40
|
+
action: () => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
41
|
+
};
|
|
42
|
+
validateMXHostname: {
|
|
43
|
+
gwta: string;
|
|
44
|
+
action: () => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
45
|
+
};
|
|
46
|
+
authenticate: {
|
|
47
|
+
gwta: string;
|
|
48
|
+
action: () => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult>;
|
|
49
|
+
};
|
|
50
|
+
spf: {
|
|
51
|
+
gwta: string;
|
|
52
|
+
action: () => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult>;
|
|
53
|
+
};
|
|
54
|
+
fetchMessage: {
|
|
55
|
+
gwta: string;
|
|
56
|
+
action: ({ uid, mailbox }: TNamed) => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
57
|
+
};
|
|
58
|
+
changeMessageFlags: {
|
|
59
|
+
gwta: string;
|
|
60
|
+
action: ({ uid, mailbox, flags }: TNamed) => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
61
|
+
};
|
|
62
|
+
moveMessage: {
|
|
63
|
+
gwta: string;
|
|
64
|
+
action: ({ uid, mailbox, where }: TNamed) => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
world?: TWorld;
|
|
68
|
+
close?(): void;
|
|
69
|
+
endFeature?(): Promise<void>;
|
|
70
|
+
onFailure?(result: import("@haibun/core/build/lib/defs.js").TStepResult, step: import("@haibun/core/build/lib/defs.js").TVStep): Promise<void | import("@haibun/core/build/lib/interfaces/logger.js").TMessageContext>;
|
|
71
|
+
getWorld(): TWorld;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
export default EmailTestingStepper;
|
|
@@ -1,125 +1,113 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const defs_1 = require("@haibun/core/build/lib/defs");
|
|
9
|
-
const util_1 = require("@haibun/core/build/lib/util");
|
|
1
|
+
import { AStepper, OK } from '@haibun/core/build/lib/defs.js';
|
|
2
|
+
import { actionNotOK, getStepperOption, stringOrError } from '@haibun/core/build/lib/util/index.js';
|
|
3
|
+
import { checkDmarc, checkDmarcField } from './lib/dmarc.js';
|
|
4
|
+
import { emailAuthenticate, emailSpf } from './lib/mailauth.js';
|
|
5
|
+
import 'dotenv/config'; // FIXME This may not be the best approach
|
|
6
|
+
import { fetchOne, moveMessage, updateMessageFlags } from './lib/imap.js';
|
|
7
|
+
import { getPolicy, validateMx } from './lib/mta.js';
|
|
10
8
|
const EMAIL_SERVER = 'EMAIL_SERVER';
|
|
11
9
|
const EMAIL_MESSAGE = 'EMAIL_MESSAGE';
|
|
12
|
-
const EmailTestingStepper = class EmailTestingStepper extends
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
const EmailTestingStepper = class EmailTestingStepper extends AStepper {
|
|
11
|
+
knownPolicy = undefined;
|
|
12
|
+
emailServer;
|
|
13
|
+
options = {
|
|
14
|
+
[EMAIL_SERVER]: {
|
|
15
|
+
desc: 'Target email server',
|
|
16
|
+
parse: (input) => stringOrError(input),
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
async setWorld(world, steppers) {
|
|
20
|
+
super.setWorld(world, steppers);
|
|
21
|
+
this.world = world;
|
|
22
|
+
this.emailServer = getStepperOption(this, EMAIL_SERVER, this.world.extraOptions);
|
|
23
|
+
}
|
|
24
|
+
async getPolicy() {
|
|
25
|
+
const res = await getPolicy(this.emailServer, this.knownPolicy);
|
|
26
|
+
return res;
|
|
27
|
+
}
|
|
28
|
+
steps = {
|
|
29
|
+
dmarcExists: {
|
|
30
|
+
gwta: `DMARC record exists`,
|
|
31
|
+
action: async () => {
|
|
32
|
+
return checkDmarc(this.emailServer);
|
|
21
33
|
},
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const record = await dmarc.fetch(this.emailServer);
|
|
29
|
-
return defs_1.OK;
|
|
30
|
-
}
|
|
31
|
-
catch (e) {
|
|
32
|
-
return (0, util_1.actionNotOK)(e.getMessage());
|
|
33
|
-
}
|
|
34
|
-
}
|
|
34
|
+
},
|
|
35
|
+
dmarcExistsFor: {
|
|
36
|
+
gwta: `DMARC record exists for {domain}`,
|
|
37
|
+
action: async ({ domain }) => {
|
|
38
|
+
return await checkDmarc(domain);
|
|
35
39
|
},
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (!record.tags[field]) {
|
|
42
|
-
return (0, util_1.actionNotOK)(`field ${field} doesn't exist`);
|
|
43
|
-
}
|
|
44
|
-
return defs_1.OK;
|
|
45
|
-
}
|
|
46
|
-
catch (e) {
|
|
47
|
-
return (0, util_1.actionNotOK)(e.getMessage());
|
|
48
|
-
}
|
|
49
|
-
}
|
|
40
|
+
},
|
|
41
|
+
dmarcHas: {
|
|
42
|
+
gwta: `DMARC field {field} is defined`,
|
|
43
|
+
action: async ({ field }) => {
|
|
44
|
+
return await checkDmarcField(this.emailServer, field);
|
|
50
45
|
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
46
|
+
},
|
|
47
|
+
dmarcHasFor: {
|
|
48
|
+
gwta: `DMARC field {field} is defined for {domain}`,
|
|
49
|
+
action: async ({ domain, field }) => {
|
|
50
|
+
return await checkDmarcField(this.emailServer, field);
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
checkMTASTS: {
|
|
54
|
+
gwta: `domain's mail server agent strict transfer security is valid`,
|
|
55
|
+
action: async () => {
|
|
56
|
+
const { policy } = await this.getPolicy();
|
|
57
|
+
for (const mx of policy.mx) {
|
|
58
|
+
const policyMatch = validateMx(mx, policy);
|
|
59
|
+
if (!policy.id) {
|
|
60
|
+
return actionNotOK(policyMatch);
|
|
63
61
|
}
|
|
64
|
-
if (policy.
|
|
65
|
-
return
|
|
62
|
+
if (policy.mx && !policyMatch) {
|
|
63
|
+
return actionNotOK(`unlisted MX ${mx}`);
|
|
66
64
|
}
|
|
67
|
-
return defs_1.OK;
|
|
68
65
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
gwta: `domain has a valid mail exchange record`,
|
|
72
|
-
action: async () => {
|
|
73
|
-
const { policy } = await this.getPolicy();
|
|
74
|
-
const res = await validateMx(this.emailServer, policy);
|
|
75
|
-
return res === true ? defs_1.OK : (0, util_1.actionNotOK)(res);
|
|
66
|
+
if (policy.mode !== 'enforce') {
|
|
67
|
+
return actionNotOK(`not using enforce mode`);
|
|
76
68
|
}
|
|
69
|
+
return OK;
|
|
77
70
|
},
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
ip: '217.146.67.33',
|
|
86
|
-
helo: 'uvn-67-33.tll01.zonevs.eu',
|
|
87
|
-
sender: 'andris@ekiri.ee',
|
|
88
|
-
// Uncomment if you do not want to provide ip/helo/sender manually but parse from the message
|
|
89
|
-
//trustReceived: true,
|
|
90
|
-
// Server processing this message, defaults to os.hostname(). Inserted into Authentication headers
|
|
91
|
-
mta: 'mx.ethereal.email',
|
|
92
|
-
// Optional DNS resolver function (defaults to `dns.promises.resolve`)
|
|
93
|
-
// resolver: async (name: any, rr: any) => await dns.promises.resolve(name, rr)
|
|
94
|
-
});
|
|
95
|
-
// output authenticated message
|
|
96
|
-
process.stdout.write(headers); // includes terminating line break
|
|
97
|
-
process.stdout.write(message);
|
|
98
|
-
return defs_1.OK;
|
|
99
|
-
}
|
|
71
|
+
},
|
|
72
|
+
validateMXHostname: {
|
|
73
|
+
gwta: `domain has a valid mail exchange record`,
|
|
74
|
+
action: async () => {
|
|
75
|
+
const { policy } = await this.getPolicy();
|
|
76
|
+
const res = await validateMx(this.emailServer, policy);
|
|
77
|
+
return res === true ? OK : actionNotOK(res);
|
|
100
78
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
79
|
+
},
|
|
80
|
+
authenticate: {
|
|
81
|
+
gwta: `authenticate`,
|
|
82
|
+
action: async () => {
|
|
83
|
+
return await emailAuthenticate();
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
spf: {
|
|
87
|
+
gwta: `spf`,
|
|
88
|
+
action: async () => {
|
|
89
|
+
return await emailSpf();
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
fetchMessage: {
|
|
93
|
+
gwta: `fetch message {uid} from {mailbox}`,
|
|
94
|
+
action: async ({ uid, mailbox }) => {
|
|
95
|
+
return await fetchOne(mailbox, parseInt(uid, 10));
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
changeMessageFlags: {
|
|
99
|
+
gwta: `change message {uid} flags in {mailbox} to {flags}`,
|
|
100
|
+
action: async ({ uid, mailbox, flags }) => {
|
|
101
|
+
return await updateMessageFlags(mailbox, parseInt(uid, 10), flags.split(','));
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
moveMessage: {
|
|
105
|
+
gwta: `move message {uid} {mailbox} to {where}`,
|
|
106
|
+
action: async ({ uid, mailbox, where }) => {
|
|
107
|
+
return await moveMessage(mailbox, parseInt(uid, 10), where);
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
124
111
|
};
|
|
125
|
-
|
|
112
|
+
export default EmailTestingStepper;
|
|
113
|
+
//# sourceMappingURL=email-testing-stepper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-testing-stepper.js","sourceRoot":"","sources":["../src/email-testing-stepper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare function checkDmarc(emailServer: string): Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
2
|
+
export declare function checkDmarcField(emailServer: string, field: any): Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { OK } from '@haibun/core/build/lib/defs.js';
|
|
2
|
+
import { actionNotOK } from '@haibun/core/build/lib/util/index.js';
|
|
3
|
+
import dmarc from 'dmarc-solution';
|
|
4
|
+
export async function checkDmarc(emailServer) {
|
|
5
|
+
try {
|
|
6
|
+
const record = await dmarc.fetch(emailServer);
|
|
7
|
+
return OK;
|
|
8
|
+
}
|
|
9
|
+
catch (e) {
|
|
10
|
+
return actionNotOK(e.getMessage());
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export async function checkDmarcField(emailServer, field) {
|
|
14
|
+
try {
|
|
15
|
+
const record = await dmarc.fetch(emailServer);
|
|
16
|
+
if (!record.tags[field]) {
|
|
17
|
+
return actionNotOK(`field ${field} doesn't exist`);
|
|
18
|
+
}
|
|
19
|
+
return OK;
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return actionNotOK(e.getMessage());
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=dmarc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dmarc.js","sourceRoot":"","sources":["../../src/lib/dmarc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAEnE,OAAO,KAAK,MAAM,gBAAgB,CAAC;AAEnC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,WAAmB;IAClD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9C,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,OAAO,WAAW,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,WAAmB,EAAE,KAAU;IACnE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,WAAW,CAAC,SAAS,KAAK,gBAAgB,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,OAAO,WAAW,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IACrC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const fetchOne: (mailbox: string, uid: number) => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
2
|
+
export declare const updateMessageFlags: (mailbox: string, uid: number, flags: string[]) => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
3
|
+
export declare const moveMessage: (mailbox: string, uid: number, destination: string) => Promise<import("@haibun/core/build/lib/defs.js").TOKActionResult | import("@haibun/core/build/lib/defs.js").TNotOKActionResult>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ImapFlow } from 'imapflow';
|
|
2
|
+
import { OK } from '@haibun/core/build/lib/defs.js';
|
|
3
|
+
import { actionNotOK } from '@haibun/core/build/lib/util/index.js';
|
|
4
|
+
const mailHost = process.env.MAIL_HOST;
|
|
5
|
+
const user = process.env.EMAIL;
|
|
6
|
+
const pass = process.env.PASSWORD;
|
|
7
|
+
const flowParams = () => {
|
|
8
|
+
if (!mailHost || !user || !pass) {
|
|
9
|
+
throw new Error('Missing environment variables');
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
host: mailHost,
|
|
13
|
+
port: 993,
|
|
14
|
+
secure: true,
|
|
15
|
+
auth: {
|
|
16
|
+
user,
|
|
17
|
+
pass,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export const fetchOne = async (mailbox, uid) => {
|
|
22
|
+
const client = new ImapFlow(flowParams());
|
|
23
|
+
await client.connect();
|
|
24
|
+
try {
|
|
25
|
+
const lock = await client.getMailboxLock(mailbox);
|
|
26
|
+
try {
|
|
27
|
+
const nuid = `${uid}`; // Convert uid to string
|
|
28
|
+
const message = await client.fetchOne(nuid, { uid: true });
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
lock.release();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return actionNotOK(error);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
await client.logout();
|
|
39
|
+
}
|
|
40
|
+
return OK;
|
|
41
|
+
};
|
|
42
|
+
export const updateMessageFlags = async (mailbox, uid, flags) => {
|
|
43
|
+
const client = new ImapFlow(flowParams());
|
|
44
|
+
await client.connect();
|
|
45
|
+
try {
|
|
46
|
+
const lock = await client.getMailboxLock(mailbox);
|
|
47
|
+
try {
|
|
48
|
+
const nuid = `${uid}`; // Convert uid to string
|
|
49
|
+
const message = await client.fetchOne(nuid, { uid: true });
|
|
50
|
+
if (message) {
|
|
51
|
+
console.log(message, `Setting flags for UID ${nuid}:`, flags);
|
|
52
|
+
// await client.messageFlagsSet({ uid: nuid }, flags);
|
|
53
|
+
// Update your existing message as needed here
|
|
54
|
+
// await updateExistingMessage(uid, { flags });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw new Error(`Message ${uid} not found`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
lock.release();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
return actionNotOK(error);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
await client.logout();
|
|
69
|
+
}
|
|
70
|
+
return OK;
|
|
71
|
+
};
|
|
72
|
+
export const moveMessage = async (mailbox, uid, destination) => {
|
|
73
|
+
const client = new ImapFlow(flowParams());
|
|
74
|
+
await client.connect();
|
|
75
|
+
const nuid = `${uid}`;
|
|
76
|
+
try {
|
|
77
|
+
const lock = await client.getMailboxLock(mailbox);
|
|
78
|
+
try {
|
|
79
|
+
const message = await client.fetchOne(nuid, { source: true });
|
|
80
|
+
if (message) {
|
|
81
|
+
await client.messageMove(nuid, destination);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw new Error(`Message ${uid} not found`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
lock.release();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
return actionNotOK(error);
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
await client.logout();
|
|
96
|
+
}
|
|
97
|
+
return OK;
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=imap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imap.js","sourceRoot":"","sources":["../../src/lib/imap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { OK } from '@haibun/core/build/lib/defs.js';
|
|
2
|
+
import { authenticate } from 'mailauth';
|
|
3
|
+
import { spf } from 'mailauth/lib/spf';
|
|
4
|
+
export async function emailAuthenticate() {
|
|
5
|
+
const message = 'hello';
|
|
6
|
+
const { dkim, spf, arc, dmarc, bimi, receivedChain, headers } = await authenticate(message, // either a String, a Buffer or a Readable Stream
|
|
7
|
+
{
|
|
8
|
+
// SMTP transmission options if available
|
|
9
|
+
ip: '217.146.67.33', // SMTP client IP
|
|
10
|
+
helo: 'uvn-67-33.tll01.zonevs.eu', // EHLO/HELO hostname
|
|
11
|
+
sender: 'andris@ekiri.ee', // MAIL FROM address
|
|
12
|
+
// Uncomment if you do not want to provide ip/helo/sender manually but parse from the message
|
|
13
|
+
//trustReceived: true,
|
|
14
|
+
// Server processing this message, defaults to os.hostname(). Inserted into Authentication headers
|
|
15
|
+
mta: 'mx.ethereal.email',
|
|
16
|
+
// Optional DNS resolver function (defaults to `dns.promises.resolve`)
|
|
17
|
+
// resolver: async (name: any, rr: any) => await dns.promises.resolve(name, rr)
|
|
18
|
+
});
|
|
19
|
+
// output authenticated message
|
|
20
|
+
process.stdout.write(headers); // includes terminating line break
|
|
21
|
+
process.stdout.write(message);
|
|
22
|
+
return OK;
|
|
23
|
+
}
|
|
24
|
+
export async function emailSpf() {
|
|
25
|
+
let result = await spf({
|
|
26
|
+
sender: 'andris@wildduck.email',
|
|
27
|
+
ip: '217.146.76.20',
|
|
28
|
+
helo: 'foo',
|
|
29
|
+
mta: 'mx.myhost.com',
|
|
30
|
+
});
|
|
31
|
+
return OK;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=mailauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mailauth.js","sourceRoot":"","sources":["../../src/lib/mailauth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,OAAO,GAAG,OAAO,CAAC;IACxB,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,MAAM,YAAY,CAChF,OAAO,EAAE,iDAAiD;IAC1D;QACE,yCAAyC;QACzC,EAAE,EAAE,eAAe,EAAE,iBAAiB;QACtC,IAAI,EAAE,2BAA2B,EAAE,qBAAqB;QACxD,MAAM,EAAE,iBAAiB,EAAE,oBAAoB;QAE/C,6FAA6F;QAC7F,sBAAsB;QAEtB,kGAAkG;QAClG,GAAG,EAAE,mBAAmB;QAExB,wEAAwE;QACxE,+EAA+E;KAChF,CACF,CAAC;IACF,+BAA+B;IAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,kCAAkC;IACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9B,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,IAAI,MAAM,GAAG,MAAM,GAAG,CAAC;QACrB,MAAM,EAAE,uBAAuB;QAC/B,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,KAAK;QACX,GAAG,EAAE,eAAe;KACrB,CAAC,CAAC;IACH,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
package/build/lib/mta.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mta.js","sourceRoot":"","sources":["../../src/lib/mta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAC,SAAS,EAAE,UAAU,EAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,53 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haibun/email-testing",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"main": "build/web-http.js",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.40.3",
|
|
6
5
|
"files": [
|
|
7
|
-
"build
|
|
6
|
+
"build/**"
|
|
8
7
|
],
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"**/__tests__ /**/*.+(ts|tsx|js)",
|
|
15
|
-
"**/?(*.)+(spec|test).+(ts|tsx|js)"
|
|
16
|
-
],
|
|
17
|
-
"transform": {
|
|
18
|
-
"^.+\\.(ts|tsx)$": "ts-jest"
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"prepublishOnly": "tsc -b .",
|
|
23
|
-
"tsc-watch": "tsc -b . --watch",
|
|
24
|
-
"test": "jest",
|
|
25
|
-
"test-watch": "jest --watch"
|
|
8
|
+
"devDependencies": {
|
|
9
|
+
"@types/node": "^20.14.10",
|
|
10
|
+
"prettier": "^3.3.2",
|
|
11
|
+
"typescript": "^5.5.3",
|
|
12
|
+
"vitest": "^2.0.1"
|
|
26
13
|
},
|
|
27
|
-
"keywords": [],
|
|
28
|
-
"author": "",
|
|
29
|
-
"license": "ISC",
|
|
30
14
|
"dependencies": {
|
|
31
|
-
"@haibun/
|
|
15
|
+
"@haibun/cli": "1.40.6",
|
|
16
|
+
"@haibun/core": "1.40.6",
|
|
17
|
+
"@types/imapflow": "^1.0.18",
|
|
32
18
|
"dmarc-solution": "^1.2.5",
|
|
33
|
-
"
|
|
19
|
+
"dotenv": "^16.4.5",
|
|
20
|
+
"imapflow": "^1.0.164",
|
|
21
|
+
"mailauth": "^4.6.8"
|
|
34
22
|
},
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"eslint-config-airbnb-typescript": "^12.0.0",
|
|
42
|
-
"eslint-config-prettier": "^8.3.0",
|
|
43
|
-
"eslint-plugin-import": "^2.22.1",
|
|
44
|
-
"eslint-plugin-prefer-arrow": "^1.2.2",
|
|
45
|
-
"eslint-plugin-prettier": "^3.3.1",
|
|
46
|
-
"jest": "^29.3.1",
|
|
47
|
-
"prettier": "^2.3.1",
|
|
48
|
-
"ts-jest": "^29.0.3",
|
|
49
|
-
"ts-node": "^10.0.0",
|
|
50
|
-
"tslint": "^6.1.3",
|
|
51
|
-
"typescript": "^4.6.3"
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test-watch": "vitest",
|
|
26
|
+
"build": "tsc",
|
|
27
|
+
"build-watch": "tsc --watch",
|
|
28
|
+
"lint": "lint --ext .ts ./src/"
|
|
52
29
|
}
|
|
53
|
-
}
|
|
30
|
+
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const lib_1 = require("@haibun/core/build/lib/test/lib");
|
|
7
|
-
const util_1 = require("@haibun/core/build/lib/util");
|
|
8
|
-
const run_1 = require("@haibun/core/build/lib/run");
|
|
9
|
-
const email_testing_stepper_1 = __importDefault(require("./email-testing-stepper"));
|
|
10
|
-
const domain = 'google.com';
|
|
11
|
-
const protoOptions = {
|
|
12
|
-
...run_1.DEF_PROTO_OPTIONS,
|
|
13
|
-
extraOptions: {
|
|
14
|
-
[(0, util_1.getStepperOptionName)(email_testing_stepper_1.default, 'EMAIL_SERVER')]: domain,
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
describe('MTA', () => {
|
|
18
|
-
it('MTA STS', async () => {
|
|
19
|
-
const features = (0, lib_1.asFeatures)([{ path: '/features/record.feature', content: `domain's mail server agent strict transfer security is valid` }]);
|
|
20
|
-
const { ok } = await (0, lib_1.testWithDefaults)(features, [email_testing_stepper_1.default], protoOptions);
|
|
21
|
-
expect(ok).toBe(true);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
describe('DMARC', () => {
|
|
25
|
-
it('Has a valid dmarc record', async () => {
|
|
26
|
-
const features = (0, lib_1.asFeatures)([{ path: '/features/record.feature', content: `DMARC record exists` }]);
|
|
27
|
-
const { ok } = await (0, lib_1.testWithDefaults)(features, [email_testing_stepper_1.default], protoOptions);
|
|
28
|
-
expect(ok).toBe(true);
|
|
29
|
-
});
|
|
30
|
-
it('DMARC rua is defined', async () => {
|
|
31
|
-
const features = (0, lib_1.asFeatures)([{ path: '/features/record.feature', content: `DMARC field rua is defined` }]);
|
|
32
|
-
const { ok } = await (0, lib_1.testWithDefaults)(features, [email_testing_stepper_1.default], protoOptions);
|
|
33
|
-
expect(ok).toBe(true);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const { authenticate } = require('mailauth');
|
|
4
|
-
const { dkimVerify } = require('mailauth/lib/dkim/verify');
|
|
5
|
-
const { spf } = require('mailauth/lib/spf');
|
|
6
|
-
const { getPolicy, validateMx } = require('mailauth/lib/mta-sts');
|
|
7
|
-
const defs_1 = require("@haibun/core/build/lib/defs");
|
|
8
|
-
const util_1 = require("@haibun/core/build/lib/util");
|
|
9
|
-
const EMAIL_SERVER = 'EMAIL_SERVER';
|
|
10
|
-
const EMAIL_MESSAGE = 'EMAIL_MESSAGE';
|
|
11
|
-
const EmailTestingStepper = class EmailTestingStepper extends defs_1.AStepper {
|
|
12
|
-
constructor() {
|
|
13
|
-
super(...arguments);
|
|
14
|
-
// requireDomains = [EMAIL_SERVER, EMAIL_MESSAGE];
|
|
15
|
-
this.options = {
|
|
16
|
-
[EMAIL_SERVER]: {
|
|
17
|
-
desc: 'Target email server',
|
|
18
|
-
parse: (input) => (0, util_1.stringOrError)(input)
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
this.steps = {
|
|
22
|
-
checkMTASTS: {
|
|
23
|
-
gwta: `domain's mail server agent strict transfer security is valid`,
|
|
24
|
-
action: async () => {
|
|
25
|
-
const { policy } = await this.getPolicy();
|
|
26
|
-
for (const mx of policy.mx) {
|
|
27
|
-
const policyMatch = validateMx(mx, policy);
|
|
28
|
-
if (!policy.id) {
|
|
29
|
-
return (0, util_1.actionNotOK)(policyMatch);
|
|
30
|
-
}
|
|
31
|
-
if (policy.mx && !policyMatch) {
|
|
32
|
-
return (0, util_1.actionNotOK)(`unlisted MX ${mx}`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
if (policy.mode !== 'enforce') {
|
|
36
|
-
return (0, util_1.actionNotOK)(`not using enforce mode`);
|
|
37
|
-
}
|
|
38
|
-
return defs_1.OK;
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
validateMXHostname: {
|
|
42
|
-
gwta: `domain has a valid mail exchange record`,
|
|
43
|
-
action: async () => {
|
|
44
|
-
const { policy } = await this.getPolicy();
|
|
45
|
-
const res = await validateMx(this.emailServer, policy);
|
|
46
|
-
return res === true ? defs_1.OK : (0, util_1.actionNotOK)(res);
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
authenticate: {
|
|
50
|
-
gwta: `authenticate`,
|
|
51
|
-
action: async () => {
|
|
52
|
-
const message = 'hello';
|
|
53
|
-
const { dkim, spf, arc, dmarc, bimi, receivedChain, headers } = await authenticate(message, // either a String, a Buffer or a Readable Stream
|
|
54
|
-
{
|
|
55
|
-
// SMTP transmission options if available
|
|
56
|
-
ip: '217.146.67.33',
|
|
57
|
-
helo: 'uvn-67-33.tll01.zonevs.eu',
|
|
58
|
-
sender: 'andris@ekiri.ee',
|
|
59
|
-
// Uncomment if you do not want to provide ip/helo/sender manually but parse from the message
|
|
60
|
-
//trustReceived: true,
|
|
61
|
-
// Server processing this message, defaults to os.hostname(). Inserted into Authentication headers
|
|
62
|
-
mta: 'mx.ethereal.email',
|
|
63
|
-
// Optional DNS resolver function (defaults to `dns.promises.resolve`)
|
|
64
|
-
// resolver: async (name: any, rr: any) => await dns.promises.resolve(name, rr)
|
|
65
|
-
});
|
|
66
|
-
// output authenticated message
|
|
67
|
-
process.stdout.write(headers); // includes terminating line break
|
|
68
|
-
process.stdout.write(message);
|
|
69
|
-
return defs_1.OK;
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
spf: {
|
|
73
|
-
gwta: `spf`,
|
|
74
|
-
action: async () => {
|
|
75
|
-
let result = await spf({
|
|
76
|
-
sender: 'andris@wildduck.email',
|
|
77
|
-
ip: '217.146.76.20',
|
|
78
|
-
helo: 'foo',
|
|
79
|
-
mta: 'mx.myhost.com'
|
|
80
|
-
});
|
|
81
|
-
return defs_1.OK;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
async setWorld(world, steppers) {
|
|
87
|
-
super.setWorld(world, steppers);
|
|
88
|
-
this.world = world;
|
|
89
|
-
this.emailServer = (0, util_1.getStepperOption)(this, EMAIL_SERVER, this.world.extraOptions);
|
|
90
|
-
}
|
|
91
|
-
async getPolicy() {
|
|
92
|
-
const knownPolicy = undefined;
|
|
93
|
-
const res = await getPolicy(this.emailServer, knownPolicy);
|
|
94
|
-
return res;
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
exports.default = EmailTestingStepper;
|