New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

uk-modulus-check

Package Overview
Dependencies
Maintainers
0
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

uk-modulus-check - npm Package Compare versions

Comparing version 1.0.16 to 2.0.0

2

package.json
{
"name": "uk-modulus-check",
"version": "1.0.16",
"version": "2.0.0",
"main": "dist/index.js",

@@ -5,0 +5,0 @@ "types": "dist/index.d.ts",

@@ -27,10 +27,16 @@ # UKModulusCheck

```
import ModulusChecker from "uk-modulus-check";
import {validateAccountDetails} from "uk-modulus-check";
const checker = new ModulusChecker();
console.log(checker.validate('180002', '00000190')); // true
console.log(checker.validate('938063', '15763217')); // false
console.log(validateAccountDetails('180002', '00000190')); // true
console.log(validateAccountDetails('938063', '15763217')); // false
```
```
const {validateAccountDetails} = require("uk-modulus-check");
console.log(validateAccountDetails('180002', '00000190')); // true
console.log(validateAccountDetails('938063', '15763217')); // false
```
## Details

@@ -37,0 +43,0 @@

@@ -23,2 +23,2 @@ {

"938654": "938621"
}
}
import { readFileSync, writeFileSync } from 'fs';
const processSubstitutionMap = () =>
{
const scsubtab = readFileSync(`${__dirname}/data/scsubtab.txt`, 'utf8')
.split('\r\n')
.map((line) => line.split(/\s+/))
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
const processSubstitutionMap = () => {
const scsubtab = readFileSync(`${__dirname}/data/scsubtab.txt`, 'utf8')
.split('\r\n')
.map((line) => line.split(/\s+/))
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
writeFileSync(`${__dirname}/data/scsubtab.json`, JSON.stringify(scsubtab, null, 2));
};
writeFileSync(
`${__dirname}/data/scsubtab.json`,
JSON.stringify(scsubtab, null, 2)
);
};
const processModulusWeights = () => {
const valacdos = readFileSync(`${__dirname}/data/valacdos-v7-90.txt`, 'utf8')
const valacdos = readFileSync(`${__dirname}/data/valacdos-v7-90.txt`, 'utf8')
.split('\r\n')

@@ -28,7 +29,9 @@ .map((line) => {

writeFileSync(`${__dirname}/data/valacdos.json`, JSON.stringify(valacdos, null, 2));
writeFileSync(
`${__dirname}/data/valacdos.json`,
JSON.stringify(valacdos, null, 2)
);
};
processSubstitutionMap();
processModulusWeights();
processModulusWeights();

@@ -28,4 +28,8 @@ import { ModulusWeight } from './interfaces';

let adjustedSortCode = sortCode;
if (modulusWeightException === 5 && substitutionMap[sortCode as keyof typeof substitutionMap]) {
adjustedSortCode = substitutionMap[sortCode as keyof typeof substitutionMap];
if (
modulusWeightException === 5 &&
substitutionMap[sortCode as keyof typeof substitutionMap]
) {
adjustedSortCode =
substitutionMap[sortCode as keyof typeof substitutionMap];
} else if (modulusWeightException === 8) {

@@ -32,0 +36,0 @@ adjustedSortCode = '090126';

@@ -11,85 +11,84 @@ import { ModulusWeight } from './interfaces';

export default class ModulusChecker {
modulusCheck = (
modulusWeight: ModulusWeight,
sortCode: string,
accountNumber: string
): boolean => {
// by default, the account details are the sort code followed by the account number
// there are exceptions to this rule, which are handled in the applyAccountDetailExceptionRules function
const accountDetails = applyAccountDetailExceptionRules(
sortCode,
accountNumber,
modulusWeight.exception
);
const modulusCalculation = (
modulusWeight: ModulusWeight,
sortCode: string,
accountNumber: string
): boolean => {
// by default, the account details are the sort code followed by the account number
const accountDetails = applyAccountDetailExceptionRules(
sortCode,
accountNumber,
modulusWeight.exception
);
// the default behaviour is to multiply and sum account details by the weight values and then carry out a modulus check
// there are adjustments, where modulus check is skipped, or weights and account details are modified
const weightValues = applyWeightValueExceptionRules(
modulusWeight,
accountDetails
);
const { modifiedAccountDetails, overwriteResult } =
applyOverwriteExceptionRules(modulusWeight, accountDetails);
if (overwriteResult !== null) return overwriteResult;
// apply weight and exception rules
const weightValues = applyWeightValueExceptionRules(
modulusWeight,
accountDetails
);
const { modifiedAccountDetails, overwriteResult } =
applyOverwriteExceptionRules(modulusWeight, accountDetails);
// multiply each digit of the account details by the corresponding weight value
const multiplicationResultArray = modifiedAccountDetails
if (overwriteResult !== null) return overwriteResult;
// multiply each digit of the account details by the corresponding weight value
const multiplicationResultArray = modifiedAccountDetails
.split('')
.map((digit, index) => parseInt(digit, 10) * weightValues[index]);
// calculate total based on the check type
let total: number;
if (modulusWeight.check_type == CheckType.DBLAL) {
total = multiplicationResultArray
.map((num) => num.toString())
.join('')
.split('')
.map((digit, index) => parseInt(digit, 10) * weightValues[index]);
.reduce((acc, digit) => acc + parseInt(digit, 10), 0);
} else {
total = multiplicationResultArray.reduce((acc, curr) => acc + curr, 0);
}
// the total is calculated differently depending on DBLAL
// in the case of DBLAL, the total is the sum of the digits of the multiplication result
// e.g. 18 -> 1 + 8 = 9 rather than 18
let total: number;
if (modulusWeight.check_type == CheckType.DBLAL) {
total = multiplicationResultArray
.map((num) => num.toString())
.join('')
.split('')
.reduce((acc, digit) => acc + parseInt(digit, 10), 0);
} else {
total = multiplicationResultArray.reduce((acc, curr) => acc + curr, 0);
}
// there are exceptions that are applied after the total has been calculated
// these can either adjust the total, or require a non-standard modulus check
const { adjustedTotal, overwriteResult2 } = applyPostTotalExceptionRules(
modulusWeight.exception,
total,
accountDetails
);
if (overwriteResult2 !== null) return overwriteResult2;
const checkTypeValue =
modulusWeight.check_type === CheckType.MOD11 ? 11 : 10;
return adjustedTotal % checkTypeValue === 0;
};
// apply post-total exception rules
const { adjustedTotal, overwriteResult2 } = applyPostTotalExceptionRules(
modulusWeight.exception,
total,
accountDetails
);
if (overwriteResult2 !== null) return overwriteResult2;
validate(sortCode: string, accountNumber: string): boolean {
// sort code must be 6 digits, account number must be between 6 and 10 digits
if (
accountNumber.length <= 6 ||
accountNumber.length >= 10 ||
sortCode.length !== 6
)
return false;
// sort code and account number must be numeric
if (!/^\d+$/.test(sortCode + accountNumber)) return false;
// find the modulus weight that matches the sort code
const matchingModulusWeights = modulusWeightsArray.filter(
(weight) =>
weight.start &&
weight.end &&
parseInt(sortCode, 10) >= weight.start &&
parseInt(sortCode, 10) <= weight.end
);
// if there are no matching modulus weights, the sort code is not recognised
// return true, since Vocalink data doesn't seem to have 100% coverage
if (!matchingModulusWeights.length) return true;
// if any of the matching modulus weights pass the modulus, the account number is valid
// note, this is slightly conservative, and might return true for some invalid account numbers
// find the actual spec. quite confusing on these cases
return matchingModulusWeights.some((weight) =>
this.modulusCheck(weight as ModulusWeight, sortCode, accountNumber)
);
}
}
const checkTypeValue = modulusWeight.check_type === CheckType.MOD11 ? 11 : 10;
return adjustedTotal % checkTypeValue === 0;
};
export const validateAccountDetails = (
sortCode: string,
accountNumber: string
): boolean => {
// sort code must be 6 digits, account number must be between 6 and 10 digits
if (
accountNumber.length <= 6 ||
accountNumber.length >= 10 ||
sortCode.length !== 6
)
return false;
// sort code and account number must be numeric
if (!/^\d+$/.test(sortCode + accountNumber)) return false;
// find the modulus weight that matches the sort code
const matchingModulusWeights = modulusWeightsArray.filter(
(weight) =>
weight.start &&
weight.end &&
parseInt(sortCode, 10) >= weight.start &&
parseInt(sortCode, 10) <= weight.end
);
// if no matching weights, assume the sort code is valid by default
if (!matchingModulusWeights.length) return true;
// check if any matching weight passes the modulus check
return matchingModulusWeights.some((weight) =>
modulusCalculation(weight as ModulusWeight, sortCode, accountNumber)
);
};

@@ -1,48 +0,9 @@

import ModulusChecker from '../src';
import { ModulusWeight } from '../src/interfaces';
import { CheckType } from '../src/enums';
import {validateAccountDetails} from '../src';
describe('ModulusChecker', () => {
let checker: ModulusChecker;
beforeEach(() => {
checker = new ModulusChecker();
});
describe('modulusCheck', () => {
const exampleWeights: ModulusWeight = {
start: 499272,
end: 499273,
check_type: CheckType.DBLAL,
exception: null,
weights: [2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1],
};
test('should pass example 1 from Vocalink spec', () => {
const result = checker.modulusCheck(exampleWeights, '499273', '12345678');
expect(result).toBe(true);
});
test('should fail example 1 from Vocalink spec with 1 increment', () => {
const result = checker.modulusCheck(exampleWeights, '499273', '1234569');
expect(result).toBe(false);
});
test('should pass example 2 from Vocalink spec', () => {
const exampleWeights: ModulusWeight = {
start: 0,
end: 1,
check_type: CheckType.MOD11,
exception: null,
weights: [0, 0, 0, 0, 0, 0, 7, 5, 8, 3, 4, 6, 2, 1],
};
const result = checker.modulusCheck(exampleWeights, '000000', '58177632');
expect(result).toBe(true);
});
});
describe('isValid', () => {
// Custom tests
test('should return false for a length 7 sort code', () => {
const isValid = checker.validate('1234567', '12345678');
const isValid = validateAccountDetails('1234567', '12345678');
expect(isValid).toBe(false);

@@ -52,3 +13,3 @@ });

test('should return false for a length 11 account number', () => {
const isValid = checker.validate('000000', '12345678910');
const isValid = validateAccountDetails('000000', '12345678910');
expect(isValid).toBe(false);

@@ -58,3 +19,3 @@ });

test('should return false for a non-numeric sort code', () => {
const isValid = checker.validate('12345a', '12345678');
const isValid = validateAccountDetails('12345a', '12345678');
expect(isValid).toBe(false);

@@ -64,3 +25,3 @@ });

test('should return true for a sort code not on the spec range', () => {
const isValid = checker.validate('000000', '12345678');
const isValid = validateAccountDetails('000000', '12345678');
expect(isValid).toBe(true);

@@ -112,3 +73,3 @@ });

test(`Vocalink spec test ${index + 1}`, () => {
const isValid = checker.validate(sortCode, accountNumber);
const isValid = validateAccountDetails(sortCode, accountNumber);
expect(isValid).toBe(expectedResult);

@@ -115,0 +76,0 @@ });

Sorry, the diff of this file is too big to display

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