Skip to content

Commit

Permalink
Switch to using cidr-matcher for SPF IP eval
Browse files Browse the repository at this point in the history
  • Loading branch information
MattIPv4 committed Jan 3, 2024
1 parent 87e32ab commit 9c09283
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 50 deletions.
132 changes: 132 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@
"dependencies": {
"abortcontroller-polyfill": "^1.7.5",
"babel-polyfill": "^6.26.0",
"cidr-matcher": "^2.1.1",
"do-bulma": "github:do-community/do-bulma",
"do-vue": "github:do-community/do-vue",
"vue": "^3.3.4",
"vue-tippy": "^6.2.0",
"web-whois": "0.0.6"
},
"devDependencies": {
"@types/cidr-matcher": "^2.1.2",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"@typescript-eslint/parser": "^6.2.1",
"@vue/component-compiler-utils": "^3.3.0",
Expand Down
63 changes: 13 additions & 50 deletions src/spf-explainer/utils/spf_sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,53 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import CIDRMatcher from "cidr-matcher"

// Defines a SPF rule.
class SPFRule {
// Defines public values in the class.
public rule: boolean | undefined
public range: RegExp

// Turns the IP range into a regex.
private _regexFromString(range: string) {
const regexParts: string[] = []
let regex = ""
range = range.split("/").shift()!
if (range.includes(".")) {
// This is a IPv4 address.
const partSplit = range.split(".")
for (const part of partSplit) {
if (part === "0") regexParts.push("[0-9]+")
else regexParts.push(part)
}
regex = `${regexParts.join("\\.")}.*`
} else {
// This is a IPv6 address.
let end: string | undefined
const parts: string[] = []

if (range.startsWith("::")) {
range = range.slice(2)
parts.push(".+")
}

if (range.endsWith("::")) {
range = range.slice(0, -2)
end = ".+"
}

parts.push(...range.split(":"))

if (end) parts.push(end)

regex = `${parts.join(":")}.*`
}
return RegExp(regex)
}
public matcher: CIDRMatcher

// Constructs the rule.
public constructor(rule: boolean | undefined, range: string) {
public constructor(rule: boolean | undefined, ips: string[]) {
this.rule = rule
this.range = this._regexFromString(range)
this.matcher = new CIDRMatcher(ips)
}
}

Expand Down Expand Up @@ -88,23 +53,21 @@ class SPFSandbox {
const ips = new Set<string>()
for (const v4 of spf.ip4 || []) ips.add(v4[0][1])
for (const v6 of spf.ip6 || []) ips.add(v6[0][1])
for (const p of ips) this._rules.push(new SPFRule(action, p))
this._rules.push(new SPFRule(action, [...ips]))
this._listeners.forEach(listener => listener())
}

// Evals the IP address/range given.
// Returns null if the IP is allowed, true for a hard fail, false for a soft fail, undefined for a neutral fail.
public eval(ip: string) {
const rulesCpy: SPFRule[] = []
for (const r of this._rules) rulesCpy.push(r)

let hardfail: undefined | boolean
for (let ruleIndex = 0; rulesCpy.length > ruleIndex; ruleIndex++) {
const rule = rulesCpy[ruleIndex]
if (ip.match(rule.range)) return null
else hardfail = rule.rule
let hardFail: undefined | boolean

for (const rule of this._rules) {
if (rule.matcher.contains(ip)) return null
hardFail = rule.rule
}

return hardfail
return hardFail
}

// Listen for imports.
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"module": "commonjs",
"target": "es2018",
"allowJs": true,
"esModuleInterop": true,
"noImplicitAny": true,
"noEmit": false,
"strict": true
Expand Down

1 comment on commit 9c09283

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit has been deployed to DigitalOcean Spaces for easy reviewing.

dns-lookup (Build Analysis Report) spf-explainer (Build Analysis Report)

Please sign in to comment.