Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

dmarc-solution

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dmarc-solution - npm Package Compare versions

Comparing version 1.0.2 to 1.0.3

46

bin/fetcher.js
var dns = require('dns');
var fetch = function (domainName){
return new Promise((resolve, reject) => {
dns.resolveTxt('_dmarc.' + domainName, (err, records) => {
if(err){
if(err.message && typeof err.message == 'string' && err.message.startsWith('queryTxt ENOTFOUND'))
return reject(new Error('DMARC Record not available'));
return reject(err.message);
}
var record = null;
for(var i=0;i<records.length;i++){
for(var j=0;j<records[i].length;j++){
if(records[i][j].startsWith('v=DMARC')){
record = records[i][j];
break;
}
}
if(record != null)
break;
}
if(record == null) return reject(new Error('DMARC Record not available'));
return resolve(record);
});
})
var fetch = function (domainName) {
return new Promise((resolve, reject) => {
dns.resolveTxt('_dmarc.' + domainName, (err, records) => {
if (err) {
if (err.message && typeof err.message == 'string' && err.message.startsWith('queryTxt ENOTFOUND'))
return reject(new Error('DMARC Record not available'));
return reject(err.message);
}
var record = null;
for (var i = 0; i < records.length; i++) {
for (var j = 0; j < records[i].length; j++) {
if (records[i][j].startsWith('v=DMARC')) {
record = records[i][j];
break;
}
}
if (record != null)
break;
}
if (record == null) return reject(new Error('DMARC Record not available'));
return resolve(record);
});
})
}
module.exports = fetch;
var validators = require('./validator');
var generate = function (values){
var record = [];
if(values.v == undefined){
throw new Error('DMARC Version is required tag');
}
validators.v.validate.call(validators.v, 'v', values.v);
record.push('v=' + values.v);
for(var i=0;i<Object.keys(values).length;i++){
var term = Object.keys(values)[i];
if(term === 'v') continue;
if(validators[term]) {
let settings = validators[term];
var value = null;
if(settings.generate){
value = settings.generate(values[term]);
}else value = values[term];
settings.validate.call(settings, term, value);
record.push(term + "=" + value);
}
}
return record.join('; ');
var generate = function (values) {
var record = [];
if (values.v == undefined) {
throw new Error('DMARC Version is required tag');
}
validators.v.validate.call(validators.v, 'v', values.v);
record.push('v=' + values.v);
for (var i = 0; i < Object.keys(values).length; i++) {
var term = Object.keys(values)[i];
if (term === 'v') continue;
if (validators[term]) {
let settings = validators[term];
var value = null;
if (settings.generate) {
value = settings.generate(values[term]);
} else value = values[term];
settings.validate.call(settings, term, value);
record.push(term + "=" + value);
}
}
return record.join('; ');
}
module.exports = generate;

@@ -5,74 +5,74 @@ 'use strict';

function parse(policy) {
// Steps
// 1. Split policy string on semicolons into term pairs
// 2. Process and validate each term pair
// Steps
// 1. Split policy string on semicolons into term pairs
// 2. Process and validate each term pair
let terms = policy.split(/;/)
.map(t => t.trim()) // Trim surrounding whitespace
.filter(x => x !== ''); // Ignore empty tags
let terms = policy.split(/;/)
.map(t => t.trim()) // Trim surrounding whitespace
.filter(x => x !== ''); // Ignore empty tags
let rules = terms.map(
x => x.split(/[=]/)
.map(p => p.trim())
);
let rules = terms.map(
x => x.split(/[=]/)
.map(p => p.trim())
);
let retval = {
tags: {},
messages: []
};
let retval = {
tags: {},
messages: []
};
// Make sure `v` is the first tag
if (!/^v$/i.test(rules[0][0])) {
retval.messages.push(`First tag in a DMARC policy must be 'v', but found: '${rules[0][0]}'`);
return retval;
}
// Make sure `v` is the first tag
if (!/^v$/i.test(rules[0][0])) {
retval.messages.push(`First tag in a DMARC policy must be 'v', but found: '${rules[0][0]}'`);
return retval;
}
for (let rule of rules) {
let term = rule[0];
let value = rule[1];
for (let rule of rules) {
let term = rule[0];
let value = rule[1];
let found = false;
let found = false;
for (let validatorTerm of Object.keys(validators)) {
let settings = validators[validatorTerm];
for (let validatorTerm of Object.keys(validators)) {
let settings = validators[validatorTerm];
// Term matches validaor
debugger;
let termRegex = new RegExp(`^${validatorTerm}$`, 'i');
if (termRegex.test(term)) {
found = true;
// Term matches validaor
debugger;
let termRegex = new RegExp(`^${validatorTerm}$`, 'i');
if (termRegex.test(term)) {
found = true;
let tag = {
// tag: term,
description: settings.description
};
let tag = {
// tag: term,
description: settings.description
};
if (settings.validate) {
try {
settings.validate.call(settings, term, value);
tag.value = value;
retval.tags[term] = tag;
}
catch (err) {
retval.messages.push(err.message);
}
}
if (settings.validate) {
try {
settings.validate.call(settings, term, value);
tag.value = value;
retval.tags[term] = tag;
}
catch (err) {
retval.messages.push(err.message);
}
}
break;
}
}
break;
}
}
if (!found) {
retval.messages.push(`Unknown tag '${term}'`);
}
}
if (!found) {
retval.messages.push(`Unknown tag '${term}'`);
}
}
// Remove "messages"
if (retval.messages.length === 0) {
delete retval.messages;
}
// Remove "messages"
if (retval.messages.length === 0) {
delete retval.messages;
}
return retval;
return retval;
}
module.exports = parse;
const validator = require('email-validator');
const validators = {
v: {
required: true,
description: 'The v tag is required and represents the protocol version. An example is v=DMARC1',
validate(term, value) {
if (value !== 'DMARC1') {
throw new Error(`Invalid DMARC version: '${value}'`);
}
}
},
fo: {
description: 'The FO tag pertains to how forensic reports are created and presented to DMARC users.',
validate(term, originalValue) {
var value = originalValue.split(':');
if(value.length <= 4){
if(!/^([01ds])$/i.test(value[0])){
throw new Error(`Invalid value for '${term}': '${originalValue}', must be colon seprated with: 0, 1, d, s`);
}
if(value.length > 1 && !/^([01ds])$/i.test(value[1])){
throw new Error(`Invalid value for '${term}': '${originalValue}', must be colon seprated with: 0, 1, d, s`);
}
if(value.length > 2 && !/^([01ds])$/i.test(value[2])){
throw new Error(`Invalid value for '${term}': '${originalValue}', must be colon seprated with: 0, 1, d, s`);
}
if(value.length > 3 && !/^([01ds])$/i.test(value[3])){
throw new Error(`Invalid value for '${term}': '${originalValue}', must be colon seprated with: 0, 1, d, s`);
}
}
},
generate(value) {
if(value && value.length)
return value.join(':');
throw new Error("Invalid for 'fo' tag")
}
},
p: {
description: 'The required p tag demonstrates the policy for domain (or requested handling policy). It directs the receiver to report, quarantine, or reject emails that fail authentication checks. Policy options are: 1) None 2) Quarantine or 3) Reject.',
validate(term, value) {
if (!/^(none|quarantine|reject)$/i.test(value)) {
throw new Error(`Invalid value for '${term}': '${value}', must be one of: none, quarantine, reject`);
}
}
},
pct: {
description: `This DMARC tag specifies the percentage of email messages subjected to filtering. For example, pct=25 means a quarter of your company’s emails will be filtered by the recipient.`,
validate(term, value) {
if (!/^\d+$/.test(value)) {
throw new Error(`Invalid value for '${term}': ${value}, must be a positive integer`);
}
else if (parseInt(value, 10) > 100 || parseInt(value, 10) < 0) {
throw new Error(`Invalid value for '${term}': ${value}, must be an integer between 0 and 100`);
}
}
},
rf: {
description: `Format to be used for message-specific failure reports (colon-separated plain-text list of values)`,
validate(term, value) {
// The RFC says the values are colon-separated but a lot of examples/docs around the net show commas... so we'll do both
let values = value.split(/,|:/).map(x => x.trim());
v: {
required: true,
description: 'The v tag is required and represents the protocol version. An example is v=DMARC1',
validate(term, value) {
if (value !== 'DMARC1') {
throw new Error(`Invalid DMARC version: '${value}'`);
}
}
},
fo: {
description: 'The FO tag pertains to how forensic reports are created and presented to DMARC users.',
validate(term, originalValue) {
var value = originalValue.split(':');
if (value.length <= 4) {
if (!/^([01ds])$/i.test(value[0])) {
throw new Error(`Invalid value for '${term}': '${originalValue}', must be colon seprated with: 0, 1, d, s`);
}
if (value.length > 1 && !/^([01ds])$/i.test(value[1])) {
throw new Error(`Invalid value for '${term}': '${originalValue}', must be colon seprated with: 0, 1, d, s`);
}
if (value.length > 2 && !/^([01ds])$/i.test(value[2])) {
throw new Error(`Invalid value for '${term}': '${originalValue}', must be colon seprated with: 0, 1, d, s`);
}
if (value.length > 3 && !/^([01ds])$/i.test(value[3])) {
throw new Error(`Invalid value for '${term}': '${originalValue}', must be colon seprated with: 0, 1, d, s`);
}
}
},
generate(value) {
if (value && value.length)
return value.join(':');
throw new Error("Invalid for 'fo' tag")
}
},
p: {
description: 'The required p tag demonstrates the policy for domain (or requested handling policy). It directs the receiver to report, quarantine, or reject emails that fail authentication checks. Policy options are: 1) None 2) Quarantine or 3) Reject.',
validate(term, value) {
if (!/^(none|quarantine|reject)$/i.test(value)) {
throw new Error(`Invalid value for '${term}': '${value}', must be one of: none, quarantine, reject`);
}
}
},
pct: {
description: `This DMARC tag specifies the percentage of email messages subjected to filtering. For example, pct=25 means a quarter of your company’s emails will be filtered by the recipient.`,
validate(term, value) {
if (!/^\d+$/.test(value)) {
throw new Error(`Invalid value for '${term}': ${value}, must be a positive integer`);
}
else if (parseInt(value, 10) > 100 || parseInt(value, 10) < 0) {
throw new Error(`Invalid value for '${term}': ${value}, must be an integer between 0 and 100`);
}
}
},
rf: {
description: `Format to be used for message-specific failure reports (colon-separated plain-text list of values)`,
validate(term, value) {
// The RFC says the values are colon-separated but a lot of examples/docs around the net show commas... so we'll do both
let values = value.split(/,|:/).map(x => x.trim());
for (let val of values) {
if (!/^(afrf|iodef)$/i.test(val)) {
throw new Error(`Invalid value for '${term}': '${value}', must be one or more of these values: afrf, iodef. Multiple values must be separated by a comma or colon`);
}
}
}
},
ri: {
description: 'The ri tag corresponds to the aggregate reporting interval and provides DMARC feedback for the outlined criteria.',
validate(term, value) {
if (!/^\d+$/.test(value)) {
throw new Error(`Invalid value for '${term}': ${value}, must be an unsigned integer`);
}
}
},
rua: {
description: 'This optional tag is designed for reporting URI(s) for aggregate data. An rua example is rua=mailto:CUSTOMER@for.example.com.',
validate(term, value) {
let values = value.split(/,/).map(x => x.trim());
for (let val of values) {
if (!/^(afrf|iodef)$/i.test(val)) {
throw new Error(`Invalid value for '${term}': '${value}', must be one or more of these values: afrf, iodef. Multiple values must be separated by a comma or colon`);
}
}
}
},
ri: {
description: 'The ri tag corresponds to the aggregate reporting interval and provides DMARC feedback for the outlined criteria.',
validate(term, value) {
if (!/^\d+$/.test(value)) {
throw new Error(`Invalid value for '${term}': ${value}, must be an unsigned integer`);
}
}
},
rua: {
description: 'This optional tag is designed for reporting URI(s) for aggregate data. An rua example is rua=mailto:CUSTOMER@for.example.com.',
validate(term, value) {
let values = value.split(/,/).map(x => x.trim());
for (let val of values) {
let matches = val.match(/^mailto:(.+)$/i);
if (!matches) {
throw new Error(`Invalid value for '${term}': ${value}, must be a list of DMARC URIs such as 'mailto:some.email@somedomain.com'`);
}
let email = matches[1];
if (!validator.validate(email)) {
throw new Error(`Invalid email address in '${term}': '${email}'`);
}
}
},
generate(value){
var mailtoList = [];
if(value && value.length){
for(var i=0;i<value.length;i++){
if(typeof value[i] === 'string'){
if(!value[i].startsWith('mailto:'))
value[i] = 'mailto:' + value[i];
if(mailtoList.indexOf(value[i]) == -1) mailtoList.push(value[i])
}else throw new Error("Invalid Email: '" + value[i] + "' for 'rua' tag");
}
return mailtoList.join(',');
}else throw new Error(`Invalid value for 'rua' tag`)
}
},
ruf: {
description: 'Like the rua tag, the ruf designation is an optional tag. It directs addresses to which message-specific forensic information is to be reported (i.e., comma-separated plain-text list of URIs). An ruf example is ruf=mailto:CUSTOMER@for.example.com.',
validate(term, value) {
let values = value.split(/,/).map(x => x.trim());
for (let val of values) {
let matches = val.match(/^mailto:(.+)$/i);
if (!matches) {
throw new Error(`Invalid value for '${term}': ${value}, must be a list of DMARC URIs such as 'mailto:some.email@somedomain.com'`);
}
let email = matches[1];
if (!validator.validate(email)) {
throw new Error(`Invalid email address in '${term}': '${email}'`);
}
}
},
generate(value) {
var mailtoList = [];
if (value && value.length) {
for (var i = 0; i < value.length; i++) {
if (typeof value[i] === 'string') {
if (!value[i].startsWith('mailto:'))
value[i] = 'mailto:' + value[i];
if (mailtoList.indexOf(value[i]) == -1) mailtoList.push(value[i])
} else throw new Error("Invalid Email: '" + value[i] + "' for 'rua' tag");
}
return mailtoList.join(',');
} else throw new Error(`Invalid value for 'rua' tag`)
}
},
ruf: {
description: 'Like the rua tag, the ruf designation is an optional tag. It directs addresses to which message-specific forensic information is to be reported (i.e., comma-separated plain-text list of URIs). An ruf example is ruf=mailto:CUSTOMER@for.example.com.',
validate(term, value) {
let values = value.split(/,/).map(x => x.trim());
for (let val of values) {
let matches = val.match(/^mailto:(.+)$/i);
if (!matches) {
throw new Error(`Invalid value for '${term}': ${value}, must be a list of DMARC URIs such as 'mailto:some.email@somedomain.com'`);
}
let email = matches[1];
if (!validator.validate(email)) {
throw new Error(`Invalid email address in '${term}': '${email}'`);
}
}
},
generate(value){
var mailtoList = [];
if(value && value.length){
for(var i=0;i<value.length;i++){
if(typeof value[i] === 'string'){
if(!value[i].startsWith('mailto:'))
value[i] = 'mailto:' + value[i];
if(mailtoList.indexOf(value[i]) == -1) mailtoList.push(value[i])
}else throw new Error("Invalid Email: '" + value[i] + "' for 'ruf' tag");
}
return mailtoList.join(',');
}else throw new Error(`Invalid value for 'ruf' tag`)
}
},
sp: {
description: 'Requested Mail Receiver policy for all subdomains. Can be "none", "quarantine", or "reject".',
validate(term, value) {
if (!/^(none|quarantine|reject)$/i.test(value)) {
throw new Error(`Invalid value for '${term}': '${value}', must be one of: none, quarantine, reject`);
}
}
},
aspf: {
description: 'The aspf tag represents alignment mode for SPF. An optional tag, aspf=r is a common example of its configuration.',
validate(term, value) {
if (!/^(s|r)$/i.test(value)) {
throw new Error(`Invalid value for '${term}': '${value}', must be one of "r" or "s"`);
}
}
},
adkim: {
description: 'Similar to aspf, the optional adkim tag is the alignment mode for the DKIM protocol. A sample tag is adkim=r.',
validate(term, value) {
if (!/^(s|r)$/i.test(value)) {
throw new Error(`Invalid value for '${term}': '${value}', must be one of "r" or "s"`);
}
}
}
for (let val of values) {
let matches = val.match(/^mailto:(.+)$/i);
if (!matches) {
throw new Error(`Invalid value for '${term}': ${value}, must be a list of DMARC URIs such as 'mailto:some.email@somedomain.com'`);
}
let email = matches[1];
if (!validator.validate(email)) {
throw new Error(`Invalid email address in '${term}': '${email}'`);
}
}
},
generate(value) {
var mailtoList = [];
if (value && value.length) {
for (var i = 0; i < value.length; i++) {
if (typeof value[i] === 'string') {
if (!value[i].startsWith('mailto:'))
value[i] = 'mailto:' + value[i];
if (mailtoList.indexOf(value[i]) == -1) mailtoList.push(value[i])
} else throw new Error("Invalid Email: '" + value[i] + "' for 'ruf' tag");
}
return mailtoList.join(',');
} else throw new Error(`Invalid value for 'ruf' tag`)
}
},
sp: {
description: 'Requested Mail Receiver policy for all subdomains. Can be "none", "quarantine", or "reject".',
validate(term, value) {
if (!/^(none|quarantine|reject)$/i.test(value)) {
throw new Error(`Invalid value for '${term}': '${value}', must be one of: none, quarantine, reject`);
}
}
},
aspf: {
description: 'The aspf tag represents alignment mode for SPF. An optional tag, aspf=r is a common example of its configuration.',
validate(term, value) {
if (!/^(s|r)$/i.test(value)) {
throw new Error(`Invalid value for '${term}': '${value}', must be one of "r" or "s"`);
}
}
},
adkim: {
description: 'Similar to aspf, the optional adkim tag is the alignment mode for the DKIM protocol. A sample tag is adkim=r.',
validate(term, value) {
if (!/^(s|r)$/i.test(value)) {
throw new Error(`Invalid value for '${term}': '${value}', must be one of "r" or "s"`);
}
}
}
};
module.exports = validators;

@@ -6,32 +6,32 @@ var parser = require('./bin/parser');

var recordParser = function (dmarcRecord) {
return new Promise((resolve, reject) => {
var result = parser(dmarcRecord);
if (result.messages && result.messages.length) return reject(result.messages);
resolve(result.tags);
});
return new Promise((resolve, reject) => {
var result = parser(dmarcRecord);
if (result.messages && result.messages.length) return reject(result.messages);
resolve(result.tags);
});
}
var recordFetcher = function (domainName) {
return new Promise((resolve, reject) => {
return fetcher(domainName)
.then(record => {
return recordParser(record).then(r => [r, record]);
})
.then(([data, record]) => {
resolve({ record: record, tags: data });
})
.catch(err => {
reject(err);
})
})
return new Promise((resolve, reject) => {
return fetcher(domainName)
.then(record => {
return recordParser(record).then(r => [r, record]);
})
.then(([data, record]) => {
resolve({ record: record, tags: data });
})
.catch(err => {
reject(err);
})
})
}
var recordGenerator = function (values) {
return new Promise((resolve, reject) => {
try {
resolve(generator(values))
} catch (err) {
reject(err.message)
}
})
return new Promise((resolve, reject) => {
try {
resolve(generator(values))
} catch (err) {
reject(err.message)
}
})
}

@@ -38,0 +38,0 @@

{
"name": "dmarc-solution",
"version": "1.0.2",
"description": "One stop solution for all DMARC Problems. This package includes fetching of dmarc record, parsing the provided/fetched record, validation of DMARC record, generating a new DMARC record",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/architjn/dmarc-solution.git"
},
"keywords": ["dmarc", "dkim", "email", "mail", "spf", "record", "parser", "txt", "dns", "generator", "report"],
"author": "Archit Jain",
"license": "ISC",
"dependencies": {
"email-validator": "^2.0.4"
}
}
"name": "dmarc-solution",
"version": "1.0.3",
"description": "One stop solution for all DMARC Problems. This package includes fetching of dmarc record, parsing the provided/fetched record, validation of DMARC record, generating a new DMARC record",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/architjn/dmarc-solution.git"
},
"keywords": [
"dmarc",
"dkim",
"email",
"mail",
"spf",
"record",
"parser",
"txt",
"dns",
"generator",
"report"
],
"author": "Archit Jain",
"license": "ISC",
"maintainers": [
{
"name": "Archit Jain",
"email": "architjn93@gmail.com"
}
],
"bugs": {
"url": "https://github.com/architjn/dmarc-solution/issues"
},
"dependencies": {
"email-validator": "^2.0.4"
}
}

@@ -1,1 +0,62 @@

# dmarc-solution-1
# dmarc-solution
> One stop solution for all DMARC Problems. This package includes fetching of dmarc record, parsing the provided/fetched record, validation of DMARC record, generating a new DMARC record
# Install
npm install --save dmarc-solution
# Usage
### DMARC Generator
```javascript
const dmarc = require('dmarc-solution');
// Generator
var generateInputs = {
v: 'DMARC1', // only required tag
p: 'quarantine', //must be one of: none, quarantine, reject
rua: ['dmarc@example.com'], //array of emails for aggregate reports
ruf: ['dmarc@example.com'], //array of emails for forensic reports
fo: ['0', '1', 'd'], //array of selected options from: 0, 1, d, s
pct: 75, //integer value from 0 to 100
rf: 'afrf', //must be one or more of these string values: afrf, iodef
ri: 1, //must be an unsigned integer
sp: 'none', //must be one of: none, quarantine, reject
aspf: 'r', //must be one of "r" or "s"
adkim: 's', //must be one of "r" or "s"
}
dmarc.generate(generateInputs)
.then(record => {
console.log(record); //v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; ruf=mailto:dmarc@example.com; fo=0:1:d; pct=75; rf=afrf; ri=1; sp=none; aspf=r; adkim=s
})
.catch(err => {
console.log(err);
});
```
### DMARC Fetcher
```javascript
const dmarc = require('dmarc-solution');
// Fetcher from DNS
dmarc.fetch('github.com')
.then(record => {
console.log(record);
})
.catch(err => {
console.log(err);
});
```
### DMARC Parser
```javascript
const dmarc = require('dmarc-solution');
// DMARC Record parser
dmarc.parse('v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com;')
.then(record => {
console.log(record); //{"v":{"description":"The v tag is required and represents the protocol version. An example is v=DMARC1","value":"DMARC1"},"p":{"description":"The required p tag demonstrates the policy for domain...","value":"quarantine"},"rua":{"description":"This optional tag is designed for reporting URI(s) for aggregate data...","value":"mailto:dmarc@example.com"}}
})
.catch(err => {
console.log(err);
});
```
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc