Skip to content

Commit 2e52a0e

Browse files
committed
Merge branch 'main' into feature-minclasses
2 parents dbe7383 + 0ad1cea commit 2e52a0e

9 files changed

Lines changed: 100134 additions & 12 deletions

File tree

package-lock.json

Lines changed: 15 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@passcert/pwrules-annotations",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"description": "This package allows the parsing of Apple's password rules.",
55
"main": "./lib/cjs/index.js",
66
"module": "./lib/esm/index.js",
@@ -9,9 +9,10 @@
99
],
1010
"scripts": {
1111
"test": "echo \"Error: no test specified\" && exit 1",
12-
"tsc": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json",
12+
"tsc": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json && npm run copy-blocklist-data",
1313
"prepublish": "npm run tsc",
14-
"test-program": "cd test-package && npm run tsc"
14+
"test-program": "cd test-package && npm run tsc",
15+
"copy-blocklist-data":"echo \"./lib/cjs/data/ ./lib/esm/data\" | xargs -n 1 cp -v src/data/*.txt"
1516
},
1617
"repository": {
1718
"type": "git",
@@ -28,5 +29,8 @@
2829
"@typescript-eslint/parser": "^4.26.0",
2930
"eslint": "^7.27.0",
3031
"typescript": "^4.3.2"
32+
},
33+
"dependencies": {
34+
"@types/node": "^16.9.1"
3135
}
3236
}

src/app.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
// Copyright (c) 2019 - 2020 Apple Inc. Licensed under MIT License.
1+
// Copyright (c) 2019 - 2021 Apple Inc. Licensed under MIT License.
22
// Adapted to typescript and Bitwarden by João Miguel P. Campos (github @mikibakaiki) for PassCert project.
33
// npm package by João Miguel P. Campos (github @mikibakaiki) for PassCert project.
44

5+
import { PasswordBlocklist } from './data/passwordBlocklist';
56
import { CustomCharacterData } from './data/customCharacterData';
6-
import { CHARACTER_CLASS_END_SENTINEL, CHARACTER_CLASS_START_SENTINEL, Identifier, PROPERTY_SEPARATOR, PROPERTY_VALUE_SEPARATOR, PROPERTY_VALUE_START_SENTINEL, RuleName, SHOULD_NOT_BE_REACHED, SPACE_CODE_POINT } from './data/data.enum';
7+
import { BlockListIdentifier, CHARACTER_CLASS_END_SENTINEL, CHARACTER_CLASS_START_SENTINEL, Identifier, PROPERTY_SEPARATOR, PROPERTY_VALUE_SEPARATOR, PROPERTY_VALUE_START_SENTINEL, RuleName, SHOULD_NOT_BE_REACHED, SPACE_CODE_POINT } from './data/data.enum';
78
import { NamedCharacterData } from './data/namedCharacterData';
89
import { RuleData } from './data/ruleData';
910

11+
1012
export class PasswordRulesParser {
1113

1214
/**
@@ -60,6 +62,9 @@ export class PasswordRulesParser {
6062
case RuleName.MIN_CLASSES:
6163
actualMinClasses = rule.value;
6264
break;
65+
case RuleName.BLOCK_LIST:
66+
newPasswordRules.push(new RuleData(RuleName.BLOCK_LIST, rule.value));
67+
6368
}
6469
}
6570
newAllowedValues = this._canonicalizedPropertyValues(newAllowedValues, suppressCopyingRequiredToAllowed);
@@ -81,13 +86,15 @@ export class PasswordRulesParser {
8186
if (minimumMaxLength !== null) {
8287
newPasswordRules.push(new RuleData(RuleName.MAX_LENGTH, minimumMaxLength));
8388
}
89+
8490
if (actualMinClasses >= 1 && actualMinClasses <= 4) {
8591
newPasswordRules.push(new RuleData(RuleName.MIN_CLASSES, actualMinClasses));
8692
} else if (actualMinClasses < 1) {
8793
newPasswordRules.push(new RuleData(RuleName.MIN_CLASSES, 1));
8894
} else {
8995
newPasswordRules.push(new RuleData(RuleName.MIN_CLASSES, 4));
9096
}
97+
9198
return newPasswordRules;
9299
}
93100

@@ -381,6 +388,14 @@ export class PasswordRulesParser {
381388
return identifier && Object.values(Identifier).includes(identifier.toLowerCase());
382389
}
383390

391+
/**
392+
* Check if the identifier is a valid one for the "blocklist" rule.
393+
* @param identifier The identifier to verify.
394+
* @returns True if it is a valid identifier. False if it is not a valid identifier.
395+
*/
396+
private _isValidBlockListPropertyValueIdentifier(identifier: string): boolean {
397+
return identifier && Object.values(BlockListIdentifier).includes(identifier.toLowerCase());
398+
}
384399

385400
/**
386401
* Parse a custom character class. These classes are defined by the user and are surrounded by squared brackets ([]).
@@ -492,6 +507,35 @@ export class PasswordRulesParser {
492507
return [propertyValues, position];
493508
}
494509

510+
/**
511+
* Parse the values given to the rule "blocklist". If it's 'default', there is a list with the 100 000 most commonly used passwords.
512+
* @param input The string that contains the rules to be parsed.
513+
* @param position The position from where to start parsing the input.
514+
* @returns Returns an array with the information about the blocklist and the last position analyzed
515+
*/
516+
private _parseBlockListPropertyValue(input: string, position: number): [string[], number] {
517+
let propertyValues = [];
518+
if (this._isIdentifierCharacter(input[position])) {
519+
let identifierStartPosition = position;
520+
let [propertyValue, index] = this._parseIdentifier(input, position);
521+
position = index;
522+
if (!this._isValidBlockListPropertyValueIdentifier(propertyValue)) {
523+
console.error("Unrecognized property value identifier: " + propertyValue);
524+
return [null, identifierStartPosition];
525+
}
526+
// TODO maybe here we can fetch the default word list and make this the value of the rule: an array of all the words
527+
if (propertyValue === 'default') {
528+
const passwordBlocklist = PasswordBlocklist.getInstance();
529+
passwordBlocklist.blocklist.forEach(pw => {
530+
propertyValues.push(pw);
531+
})
532+
} else {
533+
propertyValues.push(propertyValue);
534+
}
535+
}
536+
return [propertyValues, position];
537+
}
538+
495539
/**
496540
* Parse a password rule.
497541
* @param input The string that contains the rules to be parsed.
@@ -565,6 +609,14 @@ export class PasswordRulesParser {
565609
}
566610
return [new RuleData(property.name, property.propValue), position];
567611
}
612+
case RuleName.BLOCK_LIST: {
613+
let [blocklist, index] = this._parseBlockListPropertyValue(input, position);
614+
position = index;
615+
if (blocklist) {
616+
property.propValue = blocklist;
617+
}
618+
return [new RuleData(property.name, property.propValue), position];
619+
}
568620
}
569621
console.assert(false, SHOULD_NOT_BE_REACHED);
570622
}

0 commit comments

Comments
 (0)