Skip to content

Commit 4092624

Browse files
committed
Add DNS tests
1 parent 45568ae commit 4092624

File tree

9 files changed

+371
-4
lines changed

9 files changed

+371
-4
lines changed

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"node-fetch": "^2.6.0",
2222
"ssl-checker": "^2.0.7",
2323
"uglify-js": "^3.6.1",
24+
"whois": "^2.14.2",
2425
"xml2js": "^0.4.22"
2526
},
2627
"repository": {

Diff for: src/Pentest.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
11
import Security from './security';
2+
import DNS from './dns';
23
import HTML from './html';
34
import SEO from './seo';
45
import WordPress from './wordpress';
56
import { Result } from './Test';
67

78
interface PentestResult {
89
security: Result;
10+
dns: Result;
911
html: Result;
1012
seo: Result;
1113
wordpress: Result;
1214
}
1315

1416
class Pentest {
1517
public async run(url: string): Promise<PentestResult> {
18+
const general = new DNS();
1619
const security = new Security();
1720
const html = new HTML();
1821
const seo = new SEO();
1922
const wordPress = new WordPress();
23+
const generalResult = <Result> await general.run({ url });
2024
const securityResult = <Result> await security.run({ url });
2125
const htmlResult = <Result> await html.run({ url });
2226
const seoResult = <Result> await seo.run({ url });
2327
const wordPressResult = <Result> await wordPress.run({ url });
2428

2529
return {
2630
security: securityResult,
31+
dns: generalResult,
2732
html: htmlResult,
2833
seo: seoResult,
2934
wordpress: wordPressResult,

Diff for: src/dns/A.ts

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import dns from 'dns';
2+
import whois from 'whois';
3+
import Test, { TestParameters, Result } from '../Test';
4+
import logger from '../logger';
5+
6+
class A extends Test {
7+
public name = 'A';
8+
9+
public async test({ url }: TestParameters): Promise<Result> {
10+
logger.info(`Starting ${this.constructor.name} test...`);
11+
12+
const response = await new Promise((resolve, reject) => {
13+
dns.lookup((new URL(url).hostname), { all: true }, (err, addresses) => {
14+
if (err) {
15+
reject(err);
16+
}
17+
18+
resolve(addresses);
19+
});
20+
});
21+
22+
const addresses = await Promise.all((response as any).map(async (address: any) => {
23+
const organization = await this.getOrganization(address.address);
24+
25+
return `${address.address} - ${organization}`;
26+
}));
27+
28+
return {
29+
status: 'SUCCESS',
30+
title: this.constructor.name,
31+
description: addresses.join('\n'),
32+
};
33+
}
34+
35+
protected async getOrganization(ip: string): Promise<string> {
36+
const organization: string = await new Promise((resolve, reject) => {
37+
whois.lookup(ip, function(err, data) {
38+
if (err) {
39+
reject(err);
40+
}
41+
42+
const organization = data.split('\n')
43+
.filter((line: string) => line.includes('OrgName'))
44+
.map((line: string) => line.split(':')[1].trim())
45+
.pop();
46+
47+
resolve(organization);
48+
});
49+
});
50+
51+
return organization;
52+
}
53+
}
54+
55+
export default A;

Diff for: src/dns/DMARC.ts

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import dns from 'dns';
2+
import Test, { TestParameters, Result } from '../Test';
3+
import logger from '../logger';
4+
5+
class DMARC extends Test {
6+
public name = 'DMARC';
7+
8+
public async test({ url }: TestParameters): Promise<Result> {
9+
logger.info(`Starting ${this.constructor.name} test...`);
10+
11+
const response: any = await new Promise((resolve, reject) => {
12+
dns.resolveTxt(`_dmarc.${(new URL(url).hostname)}`, (err, records) => {
13+
if (err) {
14+
reject(err);
15+
}
16+
17+
resolve(records);
18+
});
19+
});
20+
21+
if (response.length === 0) {
22+
return {
23+
status: 'WARNING',
24+
title: this.constructor.name,
25+
description: 'No DMARC record found for this domain.',
26+
}
27+
}
28+
29+
const record = response.shift().shift();
30+
31+
if (record.includes('p=none')) {
32+
return {
33+
status: 'ERROR',
34+
title: this.constructor.name,
35+
description: 'Email that fails DMARC Compliance tests will be delivered to the recipient\'s inbox.',
36+
};
37+
}
38+
39+
if (record.includes('p=quarantine')) {
40+
return {
41+
status: 'WARNING',
42+
title: this.constructor.name,
43+
description: 'Email that fails DMARC Compliance tests will be marked as spam.',
44+
};
45+
}
46+
47+
if (record.includes('p=reject')) {
48+
return {
49+
status: 'SUCCESS',
50+
title: this.constructor.name,
51+
description: 'Email that fails DMARC Compliance tests will be rejected.',
52+
};
53+
}
54+
55+
return {
56+
status: 'ERROR',
57+
title: this.constructor.name,
58+
description: 'Invalid DMARC policy found!',
59+
};
60+
}
61+
}
62+
63+
export default DMARC;

Diff for: src/dns/NS.ts

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import whois from 'whois';
2+
import Test, { TestParameters, Result } from '../Test';
3+
import logger from '../logger';
4+
5+
class NS extends Test {
6+
public name = 'NS';
7+
8+
public async test({ url }: TestParameters): Promise<Result> {
9+
logger.info(`Starting ${this.constructor.name} test...`);
10+
11+
const nameServers = await this.getNameServers((new URL(url).hostname));
12+
13+
return {
14+
status: 'SUCCESS',
15+
title: this.constructor.name,
16+
description: nameServers.join('\n'),
17+
};
18+
}
19+
20+
protected async getNameServers(domain: string): Promise<string[]> {
21+
const nameServers: string[] = await new Promise((resolve, reject) => {
22+
whois.lookup(domain, function(err, data) {
23+
if (err) {
24+
reject(err);
25+
}
26+
27+
const nameServers = data.split('\n')
28+
.filter((line: string) => line.includes('Name Server'))
29+
.map((line: string) => line.split(':')[1].trim());
30+
31+
resolve(nameServers);
32+
});
33+
});
34+
35+
return nameServers;
36+
}
37+
}
38+
39+
export default NS;

Diff for: src/dns/RegistrationDate.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import whois from 'whois';
2+
import Test, { TestParameters, Result } from '../Test';
3+
import logger from '../logger';
4+
5+
class RegistrationDate extends Test {
6+
public name = 'RegistrationDate';
7+
8+
public async test({ url }: TestParameters): Promise<Result> {
9+
logger.info(`Starting ${this.constructor.name} test...`);
10+
11+
const registrationDate = await this.getRegistrationDate((new URL(url).hostname));
12+
13+
const diffInMs = (new Date(registrationDate)).getTime() - (new Date()).getTime();
14+
const diffInDays = diffInMs / (1000 * 60 * 60 * 24);
15+
16+
return {
17+
status: diffInDays < 7 ? 'ERROR' : diffInDays < 30 ? 'WARNING' : 'SUCCESS',
18+
title: this.constructor.name,
19+
description: `Approximately ${Math.floor(diffInDays)} days until domain expires.`,
20+
};
21+
}
22+
23+
protected async getRegistrationDate(domain: string): Promise<string> {
24+
const date: string = await new Promise((resolve, reject) => {
25+
whois.lookup(domain, function(err, data) {
26+
if (err) {
27+
reject(err);
28+
}
29+
30+
const d = data.split('\n')
31+
.filter((line: string) => line.includes('Expiration Date'))
32+
.map((line: string) => line.split(': ')[1].trim())
33+
.shift();
34+
35+
resolve(d);
36+
});
37+
});
38+
39+
return date;
40+
}
41+
}
42+
43+
export default RegistrationDate;

Diff for: src/dns/index.ts

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import Test, { TestParameters, Result } from '../Test';
2+
import A from './A';
3+
import NS from './NS';
4+
import DMARC from './DMARC';
5+
import RegistrationDate from './RegistrationDate';
6+
7+
export default class DNS extends Test {
8+
public name = 'DNS';
9+
10+
constructor() {
11+
super();
12+
this.tests = [
13+
new RegistrationDate(),
14+
new NS(),
15+
new A(),
16+
new DMARC(),
17+
];
18+
}
19+
20+
public async test(params: TestParameters): Promise<Result> {
21+
const tests = this.getTests();
22+
const results = [];
23+
24+
for (const test of tests) {
25+
let result = null;
26+
27+
try {
28+
result = await test.run(params);
29+
} catch (error) {
30+
result = {
31+
status: 'ERROR',
32+
title: test.name,
33+
description: 'Test failed or cannot be run!',
34+
}
35+
}
36+
37+
results.push(result);
38+
}
39+
40+
return {
41+
status: this.getStatus(results.map(result => result.status)),
42+
title: this.name,
43+
description: '',
44+
results,
45+
};
46+
}
47+
}

Diff for: src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ program
4848
const results = await pentest.run(url);
4949

5050
const report = Report.get(config.report.format);
51-
report.write([ results.security, results.html, results.seo, results.wordpress ]);
51+
report.write([ results.security, results.dns, results.html, results.seo, results.wordpress ]);
5252
});
5353

5454
program

0 commit comments

Comments
 (0)