diff --git a/README.md b/README.md index c6db161..f60a274 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,8 @@ const allColors = cssContent ### Contact info -- __`emailRE`__: Match a valid email. Provides matching groups 1 (user name) and 2 (domain). When using the partial string to create a RE, you must use the 'u' flag. +- __`emailRE`__: Match most valid emails. Provides matching groups 1 (user name) and 2 (domain). When using the partial string to create a RE, you must use the 'u' flag. +- __`emailRFC5322RE`__: Match any valid email address as defined by [RFC 5322](https://www.rfc-editor.org/rfc/rfc5322). This is a large, complicated RE, you may want to use [`emailRE`](#emailre) in most cases. - __`usPhoneRE`__: Matches US phone numbers with optional country code and area code. - __`zipCodeRE`__: Matches 5 or 9 digit US zip codes. @@ -114,7 +115,11 @@ const allColors = cssContent - __`ipRE`__: Matches a valid, non-localhost IP address. - __`subdomainLabelRE`__: Matches a registerable domain name. Partially enforces the 63 byte domain label limit, but this is only valid for non-international (all ASCII) labels because we can only count characters. See [domain name rules](#domain-name-rules). When using the partial string to create a RE, you must use the 'u' or 'v' flag. - __`tldNameRE`__: Matches a Top Level Domain (TLD). See [domain name rules](#domain-name-rules). When using the partial string to create a RE, you must use the 'u' or 'v' flag. -- __`urlRE`__: Matches a valid URL. +- __`urlRE`__: Matches a valid URL. When using the partial string to create a RE, you must use the 'u' or 'v' flag. + +### export const fqDomainNameREString = `(?![0-9\\p{L}.\\-]{256,})(? + +- __`fqDomainNameREString`__: ${subdomainLabelREString}\\.)+${tldNameREString}` ## Domain name rules @@ -127,5 +132,3 @@ Unfortunately, there isn't clear consensus on what is allowed in a subdomain vs * are composed of alpha-numeric characters (any Unicode letter plus 0-9) and hyphens ('-'), except * the label must begin and end with an alpha-numeric character (any Uniced letter, 0-9; no hyphens), and * the label must not have consecutive hyphens in the 3rd and 4th position. E.g. 'xy--z' is invalid. - -Also note, the base domain label REs do not support International Domain Names (IDNs; also called 'special character domain names'). \ No newline at end of file diff --git a/make/10-resources.mk b/make/10-resources.mk index 63cf830..9b846ae 100644 --- a/make/10-resources.mk +++ b/make/10-resources.mk @@ -1,14 +1,14 @@ # This file was generated by @liquid-labs/catalyst-builder-node. Refer to # https://npmjs.com/package/@liquid-labs/catalyst-builder-node for further details -CATALYST_BABEL:=npx babel -CATALYST_BABEL_CONFIG:=$(shell npm explore @liquid-labs/sdlc-resource-babel-and-rollup -- pwd)/dist/babel/babel.config.cjs +SDLC_BABEL:=npx babel +SDLC_BABEL_CONFIG:=$(shell npm explore @liquid-labs/sdlc-resource-babel-and-rollup -- pwd)/dist/babel/babel.config.cjs -CATALYST_ROLLUP:=npx rollup -CATALYST_ROLLUP_CONFIG:=$(shell npm explore @liquid-labs/sdlc-resource-babel-and-rollup -- pwd)/dist/rollup/rollup.config.mjs +SDLC_ROLLUP:=npx rollup +SDLC_ROLLUP_CONFIG:=$(shell npm explore @liquid-labs/sdlc-resource-babel-and-rollup -- pwd)/dist/rollup/rollup.config.mjs -CATALYST_JEST:=npx jest -CATALYST_JEST_CONFIG:=$(shell npm explore @liquid-labs/sdlc-resource-jest -- pwd)/dist/jest.config.js +SDLC_JEST:=npx jest +SDLC_JEST_CONFIG:=$(shell npm explore @liquid-labs/sdlc-resource-jest -- pwd)/dist/jest.config.js -CATALYST_ESLINT:=npx eslint -CATALYST_ESLINT_CONFIG:=$(shell npm explore @liquid-labs/sdlc-resource-eslint -- pwd)/dist/eslint.config.cjs +SDLC_ESLINT:=npx eslint +SDLC_ESLINT_CONFIG:=$(shell npm explore @liquid-labs/sdlc-resource-eslint -- pwd)/dist/eslint.config.cjs diff --git a/make/15-data-finder.mk b/make/15-data-finder.mk index 66d1ee5..85c44ea 100644 --- a/make/15-data-finder.mk +++ b/make/15-data-finder.mk @@ -1,8 +1,8 @@ # This file was generated by @liquid-labs/catalyst-builder-node. Refer to # https://npmjs.com/package/@liquid-labs/catalyst-builder-node for further details -CATALYST_DATA_SELECTOR=\( -path "*/test/data/*" -o -path "*/test/data-*/*" -o -path "*/test-data/*" \) +SDLC_DATA_SELECTOR=\( -path "*/test/data/*" -o -path "*/test/data-*/*" -o -path "*/test-data/*" \) # all test data (cli and lib) -CATALYST_TEST_DATA_SRC:=$(shell find $(SRC) -type f $(CATALYST_DATA_SELECTOR)) -CATALYST_TEST_DATA_BUILT:=$(patsubst $(SRC)/%, $(TEST_STAGING)/%, $(CATALYST_TEST_DATA_SRC)) +SDLC_TEST_DATA_SRC:=$(shell find $(SRC) -type f $(SDLC_DATA_SELECTOR)) +SDLC_TEST_DATA_BUILT:=$(patsubst $(SRC)/%, $(TEST_STAGING)/%, $(SDLC_TEST_DATA_SRC)) diff --git a/make/20-js-src-finder.mk b/make/20-js-src-finder.mk index ed55cb1..fea420e 100644 --- a/make/20-js-src-finder.mk +++ b/make/20-js-src-finder.mk @@ -1,10 +1,10 @@ # This file was generated by @liquid-labs/catalyst-builder-node. Refer to # https://npmjs.com/package/@liquid-labs/catalyst-builder-node for further details -CATALYST_JS_SELECTOR=\( -name "*.js" -o -name "*.cjs" -o -name "*.mjs" \) -CATALYST_TEST_SELECTOR=\( -name "*.test.*js" -o -path "*/test/*" \) +SDLC_JS_SELECTOR=\( -name "*.js" -o -name "*.cjs" -o -name "*.mjs" \) +SDLC_TEST_SELECTOR=\( -name "*.test.*js" -o -path "*/test/*" \) # all source, non-test files (cli and lib) -CATALYST_ALL_JS_FILES_SRC:=$(shell find $(SRC) $(CATALYST_JS_SELECTOR) -not $(CATALYST_DATA_SELECTOR) -type f) -CATALYST_ALL_NON_TEST_JS_FILES_SRC:=$(shell find $(SRC) $(CATALYST_JS_SELECTOR) -not $(CATALYST_DATA_SELECTOR) -not $(CATALYST_TEST_SELECTOR) -type f) -CATALYST_JS_TEST_FILES_BUILT:=$(patsubst %.cjs, %.js, $(patsubst %.mjs, %.js, $(patsubst $(SRC)/%, test-staging/%, $(CATALYST_ALL_JS_FILES_SRC)))) +SDLC_ALL_JS_FILES_SRC:=$(shell find $(SRC) $(SDLC_JS_SELECTOR) -not $(SDLC_DATA_SELECTOR) -type f) +SDLC_ALL_NON_TEST_JS_FILES_SRC:=$(shell find $(SRC) $(SDLC_JS_SELECTOR) -not $(SDLC_DATA_SELECTOR) -not $(SDLC_TEST_SELECTOR) -type f) +SDLC_JS_TEST_FILES_BUILT:=$(patsubst %.cjs, %.js, $(patsubst %.mjs, %.js, $(patsubst $(SRC)/%, test-staging/%, $(SDLC_ALL_JS_FILES_SRC)))) diff --git a/make/50-readme.mk b/make/50-readme.mk index e7ad424..68942b6 100644 --- a/make/50-readme.mk +++ b/make/50-readme.mk @@ -2,10 +2,10 @@ README_SRC:=$(SRC)/doc/README.01.md $(SRC)/doc/README.02.md README_BUILT:=README.md DOC_EXTRACTOR:=$(SRC)/doc-extractor/doc-extractor.mjs -$(README_BUILT): $(README_SRC) $(DOC_EXTRACTOR) $(CATALYST_ALL_NON_TEST_JS_FILES_SRC) +$(README_BUILT): $(README_SRC) $(DOC_EXTRACTOR) $(SDLC_ALL_NON_TEST_JS_FILES_SRC) cp $< $@ node $(DOC_EXTRACTOR) cat $(SRC)/doc/README.02.md >> $@ -BUILD_TARGETS:=$(README_BUILT) \ No newline at end of file +BUILD_TARGETS+=$(README_BUILT) \ No newline at end of file diff --git a/make/50-regex-repo-js.mk b/make/50-regex-repo-js.mk index 893a8ca..3ea062b 100644 --- a/make/50-regex-repo-js.mk +++ b/make/50-regex-repo-js.mk @@ -5,14 +5,14 @@ # build dist/regex-repo.js ##### -CATALYST_REGEX_REPO_JS:=$(DIST)/regex-repo.js -CATALYST_REGEX_REPO_JS_ENTRY=$(SRC)/index.js -BUILD_TARGETS+=$(CATALYST_REGEX_REPO_JS) +SDLC_REGEX_REPO_JS:=$(DIST)/regex-repo.js +SDLC_REGEX_REPO_JS_ENTRY=$(SRC)/index.js +BUILD_TARGETS+=$(SDLC_REGEX_REPO_JS) -$(CATALYST_REGEX_REPO_JS): package.json $(CATALYST_ALL_NON_TEST_JS_FILES_SRC) - JS_BUILD_TARGET=$(CATALYST_REGEX_REPO_JS_ENTRY) \ +$(SDLC_REGEX_REPO_JS): package.json $(SDLC_ALL_NON_TEST_JS_FILES_SRC) + JS_BUILD_TARGET=$(SDLC_REGEX_REPO_JS_ENTRY) \ JS_OUT=$@ \ - $(CATALYST_ROLLUP) --config $(CATALYST_ROLLUP_CONFIG) + $(SDLC_ROLLUP) --config $(SDLC_ROLLUP_CONFIG) ##### # end dist/regex-repo.js diff --git a/make/55-lint.mk b/make/55-lint.mk index e8eee56..9a6e848 100644 --- a/make/55-lint.mk +++ b/make/55-lint.mk @@ -5,26 +5,26 @@ # lint rules ##### -CATALYST_LINT_REPORT:=$(QA)/lint.txt -CATALYST_LINT_PASS_MARKER:=$(QA)/.lint.passed -LINT_TARGETS+=$(CATALYST_LINT_REPORT) $(CATALYST_LINT_PASS_MARKER) -PRECIOUS_TARGETS+=$(CATALYST_LINT_REPORT) +SDLC_LINT_REPORT:=$(QA)/lint.txt +SDLC_LINT_PASS_MARKER:=$(QA)/.lint.passed +LINT_TARGETS+=$(SDLC_LINT_REPORT) $(SDLC_LINT_PASS_MARKER) +PRECIOUS_TARGETS+=$(SDLC_LINT_REPORT) -$(CATALYST_LINT_REPORT) $(CATALYST_LINT_PASS_MARKER): $(CATALYST_ALL_JS_FILES_SRC) +$(SDLC_LINT_REPORT) $(SDLC_LINT_PASS_MARKER): $(SDLC_ALL_JS_FILES_SRC) mkdir -p $(dir $@) - echo -n 'Test git rev: ' > $(CATALYST_LINT_REPORT) - git rev-parse HEAD >> $(CATALYST_LINT_REPORT) + echo -n 'Test git rev: ' > $(SDLC_LINT_REPORT) + git rev-parse HEAD >> $(SDLC_LINT_REPORT) ( set -e; set -o pipefail; \ - ESLINT_USE_FLAT_CONFIG=true $(CATALYST_ESLINT) \ - --config $(CATALYST_ESLINT_CONFIG) \ + ESLINT_USE_FLAT_CONFIG=true $(SDLC_ESLINT) \ + --config $(SDLC_ESLINT_CONFIG) \ . \ - | tee -a $(CATALYST_LINT_REPORT); \ - touch $(CATALYST_LINT_PASS_MARKER) ) + | tee -a $(SDLC_LINT_REPORT); \ + touch $(SDLC_LINT_PASS_MARKER) ) lint-fix: @( set -e; set -o pipefail; \ - ESLINT_USE_FLAT_CONFIG=true $(CATALYST_ESLINT) \ - --config $(CATALYST_ESLINT_CONFIG) \ + ESLINT_USE_FLAT_CONFIG=true $(SDLC_ESLINT) \ + --config $(SDLC_ESLINT_CONFIG) \ --fix . ) ##### diff --git a/make/55-test.mk b/make/55-test.mk index c56f39b..1d57346 100644 --- a/make/55-test.mk +++ b/make/55-test.mk @@ -5,48 +5,48 @@ # test rules ##### -CATALYST_TEST_REPORT:=$(QA)/unit-test.txt -CATALYST_TEST_PASS_MARKER:=$(QA)/.unit-test.passed -CATALYST_COVERAGE_REPORTS:=$(QA)/coverage -TEST_TARGETS+=$(CATALYST_TEST_REPORT) $(CATALYST_TEST_PASS_MARKER) $(CATALYST_COVERAGE_REPORTS) -PRECIOUS_TARGETS+=$(CATALYST_TEST_REPORT) +SDLC_TEST_REPORT:=$(QA)/unit-test.txt +SDLC_TEST_PASS_MARKER:=$(QA)/.unit-test.passed +SDLC_COVERAGE_REPORTS:=$(QA)/coverage +TEST_TARGETS+=$(SDLC_TEST_REPORT) $(SDLC_TEST_PASS_MARKER) $(SDLC_COVERAGE_REPORTS) +PRECIOUS_TARGETS+=$(SDLC_TEST_REPORT) -CATALYST_TEST_FILES_BUILT:=$(patsubst %.cjs, %.js, $(patsubst %.mjs, %.js, $(patsubst $(SRC)/%, $(TEST_STAGING)/%, $(CATALYST_ALL_JS_FILES_SRC)))) +SDLC_TEST_FILES_BUILT:=$(patsubst %.cjs, %.js, $(patsubst %.mjs, %.js, $(patsubst $(SRC)/%, $(TEST_STAGING)/%, $(SDLC_ALL_JS_FILES_SRC)))) -$(CATALYST_TEST_DATA_BUILT): $(TEST_STAGING)/%: $(SRC)/% +$(SDLC_TEST_DATA_BUILT): $(TEST_STAGING)/%: $(SRC)/% @echo "Copying test data..." @mkdir -p $(dir $@) @cp $< $@ # Jest is not picking up the external maps, so we inline them for the test. (As of?) -$(CATALYST_TEST_FILES_BUILT) &: $(CATALYST_ALL_JS_FILES_SRC) +$(SDLC_TEST_FILES_BUILT) &: $(SDLC_ALL_JS_FILES_SRC) rm -rf $(TEST_STAGING) mkdir -p $(TEST_STAGING) - NODE_ENV=test $(CATALYST_BABEL) \ - --config-file=$(CATALYST_BABEL_CONFIG) \ + NODE_ENV=test $(SDLC_BABEL) \ + --config-file=$(SDLC_BABEL_CONFIG) \ --out-dir=./$(TEST_STAGING) \ --source-maps=inline \ $(SRC) # remove because it's not really part of the test rm $(TEST_STAGING)/doc-extractor/doc-extractor.js -$(CATALYST_TEST_PASS_MARKER) $(CATALYST_TEST_REPORT) $(TEST_STAGING)/coverage &: package.json $(CATALYST_TEST_FILES_BUILT) $(CATALYST_TEST_DATA_BUILT) +$(SDLC_TEST_PASS_MARKER) $(SDLC_TEST_REPORT) $(TEST_STAGING)/coverage &: package.json $(SDLC_TEST_FILES_BUILT) $(SDLC_TEST_DATA_BUILT) rm -rf $@ mkdir -p $(dir $@) - echo -n 'Test git rev: ' > $(CATALYST_TEST_REPORT) - git rev-parse HEAD >> $(CATALYST_TEST_REPORT) + echo -n 'Test git rev: ' > $(SDLC_TEST_REPORT) + git rev-parse HEAD >> $(SDLC_TEST_REPORT) ( set -e; set -o pipefail; \ - ( cd $(TEST_STAGING) && $(CATALYST_JEST) \ - --config=$(CATALYST_JEST_CONFIG) \ + ( cd $(TEST_STAGING) && $(SDLC_JEST) \ + --config=$(SDLC_JEST_CONFIG) \ --runInBand \ $(TEST) 2>&1 ) \ - | tee -a $(CATALYST_TEST_REPORT); \ - touch $(CATALYST_TEST_PASS_MARKER) ) + | tee -a $(SDLC_TEST_REPORT); \ + touch $(SDLC_TEST_PASS_MARKER) ) -$(CATALYST_COVERAGE_REPORTS): $(CATALYST_TEST_PASS_MARKER) $(TEST_STAGING)/coverage - rm -rf $(CATALYST_COVERAGE_REPORTS) - mkdir -p $(CATALYST_COVERAGE_REPORTS) - cp -r $(TEST_STAGING)/coverage/* $(CATALYST_COVERAGE_REPORTS) +$(SDLC_COVERAGE_REPORTS): $(SDLC_TEST_PASS_MARKER) $(TEST_STAGING)/coverage + rm -rf $(SDLC_COVERAGE_REPORTS) + mkdir -p $(SDLC_COVERAGE_REPORTS) + cp -r $(TEST_STAGING)/coverage/* $(SDLC_COVERAGE_REPORTS) ##### # end test diff --git a/package-lock.json b/package-lock.json index dd2a298..e7985a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "3.0.0", "license": "Apache-2.0", "devDependencies": { - "@liquid-labs/sdlc-resource-babel-and-rollup": "^1.0.0-alpha.8", + "@liquid-labs/sdlc-resource-babel-and-rollup": "^1.0.0-alpha.9", "@liquid-labs/sdlc-resource-eslint": "^1.0.0-alpha.10", "@liquid-labs/sdlc-resource-jest": "^1.0.0-alpha.7" }, @@ -2969,9 +2969,9 @@ } }, "node_modules/@liquid-labs/sdlc-resource-babel-and-rollup": { - "version": "1.0.0-alpha.8", - "resolved": "https://registry.npmjs.org/@liquid-labs/sdlc-resource-babel-and-rollup/-/sdlc-resource-babel-and-rollup-1.0.0-alpha.8.tgz", - "integrity": "sha512-u4/s+Wojii9rLBHP/JGuwfh6MxoG5XpvoRJUwrKk9zFVawEvLhV63xu6NTxhktY9Gd6pHrUijNfHbEF5HMATUw==", + "version": "1.0.0-alpha.9", + "resolved": "https://registry.npmjs.org/@liquid-labs/sdlc-resource-babel-and-rollup/-/sdlc-resource-babel-and-rollup-1.0.0-alpha.9.tgz", + "integrity": "sha512-IEDiFYWok+mPyWcPRilOu9EhMcD1Y92OM3xqrjH113qjp/OtKOTMYqXNyU1xSsZWOSMU3ejCOH5n0S2yNtF/KQ==", "dev": true, "dependencies": { "@babel/cli": "^7.23.4", @@ -13262,9 +13262,9 @@ } }, "@liquid-labs/sdlc-resource-babel-and-rollup": { - "version": "1.0.0-alpha.8", - "resolved": "https://registry.npmjs.org/@liquid-labs/sdlc-resource-babel-and-rollup/-/sdlc-resource-babel-and-rollup-1.0.0-alpha.8.tgz", - "integrity": "sha512-u4/s+Wojii9rLBHP/JGuwfh6MxoG5XpvoRJUwrKk9zFVawEvLhV63xu6NTxhktY9Gd6pHrUijNfHbEF5HMATUw==", + "version": "1.0.0-alpha.9", + "resolved": "https://registry.npmjs.org/@liquid-labs/sdlc-resource-babel-and-rollup/-/sdlc-resource-babel-and-rollup-1.0.0-alpha.9.tgz", + "integrity": "sha512-IEDiFYWok+mPyWcPRilOu9EhMcD1Y92OM3xqrjH113qjp/OtKOTMYqXNyU1xSsZWOSMU3ejCOH5n0S2yNtF/KQ==", "dev": true, "requires": { "@babel/cli": "^7.23.4", diff --git a/package.json b/package.json index 7881a47..6a35a33 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "orgKey": "@liquid-labs" }, "devDependencies": { - "@liquid-labs/sdlc-resource-babel-and-rollup": "^1.0.0-alpha.8", + "@liquid-labs/sdlc-resource-babel-and-rollup": "^1.0.0-alpha.9", "@liquid-labs/sdlc-resource-eslint": "^1.0.0-alpha.10", "@liquid-labs/sdlc-resource-jest": "^1.0.0-alpha.7" } diff --git a/src/contacts.js b/src/contacts.js index 40e33d3..15941eb 100644 --- a/src/contacts.js +++ b/src/contacts.js @@ -14,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { fqDomainNameREString } from './web' import { lockdownRE } from './lib/lockdown-re' +import { uniNonASCII } from './lib/uni-non-ascii' export const usPhoneREString = '(\\+?1[._ -]?)?(\\(\\d{3}\\)|\\d{3})[._ -]?\\d{3}[._ -]?\\d{4}' // Contact info: Matches US phone numbers with optional country code and area code. @@ -23,3 +25,7 @@ export const usPhoneRE = lockdownRE(usPhoneREString) export const zipCodeREString = '\\d{5}([._ -]?\\d{4})?' // Contact info: Matches 5 or 9 digit US zip codes. export const zipCodeRE = lockdownRE(zipCodeREString) + +export const emailREString = `([a-zA-Z0-9${uniNonASCII}!#$%&'*+\\/=?^_\`\\{\\|\\}~\\-]+(?:\\.[a-zA-Z0-9${uniNonASCII}!#$%&'*+\\/=?^_\`\\{\\|\\}~\\-]+)*|"(?:[\\x20-\\x21\\x23-\\x5b\\x5d-\\x7e${uniNonASCII}]|\\\\[\\x20-\\x7e${uniNonASCII}])*")@(${fqDomainNameREString})` +// Contact info: Match most valid emails. Provides matching groups 1 (user name) and 2 (domain). When using the partial string to create a RE, you must use the 'u' flag. +export const emailRE = lockdownRE(emailREString, 'u') diff --git a/src/doc/README.02.md b/src/doc/README.02.md index 8055eb6..bd38376 100644 --- a/src/doc/README.02.md +++ b/src/doc/README.02.md @@ -9,5 +9,3 @@ Unfortunately, there isn't clear consensus on what is allowed in a subdomain vs * are composed of alpha-numeric characters (any Unicode letter plus 0-9) and hyphens ('-'), except * the label must begin and end with an alpha-numeric character (any Uniced letter, 0-9; no hyphens), and * the label must not have consecutive hyphens in the 3rd and 4th position. E.g. 'xy--z' is invalid. - -Also note, the base domain label REs do not support International Domain Names (IDNs; also called 'special character domain names'). \ No newline at end of file diff --git a/src/lib/uni-non-ascii.mjs b/src/lib/uni-non-ascii.mjs new file mode 100644 index 0000000..ea06cc9 --- /dev/null +++ b/src/lib/uni-non-ascii.mjs @@ -0,0 +1 @@ +export const uniNonASCII = '\\u0080-\\u{e007f}' diff --git a/src/test/contacts.test.js b/src/test/contacts.test.js index ba62c1c..e714cc3 100644 --- a/src/test/contacts.test.js +++ b/src/test/contacts.test.js @@ -16,6 +16,7 @@ limitations under the License. import { groupTest, groupTestPartial } from './lib/test-lib' import * as regex from '../contacts' +import { goodEmails, badEmails } from './data/emails' import { goodUsPhones, badUsPhones } from './data/usPhones' import { goodZipCodes, badZipCodes } from './data/zipCodes' @@ -24,3 +25,6 @@ groupTestPartial(regex.usPhoneREString, goodUsPhones, badUsPhones, 'US phones') groupTest(regex.zipCodeRE, goodZipCodes, badZipCodes, 'US zip codes') groupTestPartial(regex.zipCodeREString, goodZipCodes, badZipCodes, 'US zip codes') + +groupTest(regex.emailRE, goodEmails, badEmails, 'emails') +groupTestPartial(regex.emailREString, goodEmails, badEmails, 'emails', undefined, undefined, 'u') diff --git a/src/test/data/emails.js b/src/test/data/emails.js index 8a4249a..014a10d 100644 --- a/src/test/data/emails.js +++ b/src/test/data/emails.js @@ -22,7 +22,8 @@ export const goodEmails = [ 'foo-_18+Z.t%c@Bart-teg38w.co', 'foo@some.reallylongtld', 'foo@subb-sub.sub.com', - 'foo@some.reallylongtld' + 'foo@some.reallylongtld', + '"quote@username"@foo.com' ] export const badEmails = [ @@ -33,3 +34,12 @@ export const badEmails = [ const badDomainChars = ['_', '@', '+', '%'] badDomainChars.forEach((c) => badEmails.push(`foo@bar${c}baz.com`)) + +export const goodEmailsRFC5322 = [ + ...goodEmails, + '(comment)foo(comment2)@(comment3)[123.123.123.123](comment4)' +] + +export const badEmailsRFC5322 = [ + ...badEmails +] diff --git a/src/test/data/tlds.js b/src/test/data/tlds.js index 74e830a..b68732a 100644 --- a/src/test/data/tlds.js +++ b/src/test/data/tlds.js @@ -1,6 +1,7 @@ export const goodTLDs = [ 'com', 'cc', + 'a1b', '域', 'alongtld', 'abcdefghijklmnopqprsuvwxyzabcdefghijklmnopqprsuvwxyzabcdefghijk' // 63 characters OK @@ -8,7 +9,6 @@ export const goodTLDs = [ export const badTLDs = [ 'a', - 'a1b', '1a', 'a-z', '-az', diff --git a/src/test/web.test.js b/src/test/web.test.js index 19bee6e..e8c99fb 100644 --- a/src/test/web.test.js +++ b/src/test/web.test.js @@ -17,7 +17,6 @@ limitations under the License. import { groupTest, groupTestPartial } from './lib/test-lib' import * as regex from '../web' import { goodDomainNames, badDomainNames } from './data/domains' -import { goodEmails, badEmails } from './data/emails' import { goodFQDomainNames, badFQDomainNames } from './data/fq-domains' import { goodIPs, badIPs } from './data/ips' import { goodIPFormats, badIPFormats } from './data/ip-formats' @@ -27,9 +26,6 @@ import { goodUrls, badUrls } from './data/urls' groupTest(regex.subdomainLabelRE, goodDomainNames, badDomainNames, 'subdomain label') groupTestPartial(regex.subdomainLabelREString, goodDomainNames, badDomainNames, 'subdomain label', undefined, undefined, 'u') -groupTest(regex.emailRE, goodEmails, badEmails, 'emails') -groupTestPartial(regex.emailREString, goodEmails, badEmails, 'emails', undefined, undefined, 'u') - groupTest(regex.fqDomainNameRE, goodFQDomainNames, badFQDomainNames, 'FQ domain names') groupTestPartial(regex.fqDomainNameREString, goodFQDomainNames, badFQDomainNames, 'FQ domain names', undefined, undefined, 'u') diff --git a/src/web.js b/src/web.js index ff5fa1e..3d26400 100644 --- a/src/web.js +++ b/src/web.js @@ -15,6 +15,7 @@ limitations under the License. */ import { lockdownRE } from './lib/lockdown-re' +import { uniNonASCII } from './lib/uni-non-ascii' export const ipREString = '(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' + '(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' + @@ -40,27 +41,23 @@ export const urlREString = // excludes network & broacast addresses // (first & last IP address of each class) '(?:(?:' + ipREString + - '|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?' -// Web: Matches a valid URL. -export const urlRE = lockdownRE(urlREString, 'i') + `|(?:(?:[a-z${uniNonASCII}0-9]+-?)*[a-z${uniNonASCII}0-9]+)(?:\\.(?:[a-z${uniNonASCII}0-9]+-?)*[a-z${uniNonASCII}0-9]+)*(?:\\.(?:[a-z${uniNonASCII}]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?` +// Web: Matches a valid URL. When using the partial string to create a RE, you must use the 'u' or 'v' flag. +export const urlRE = lockdownRE(urlREString, 'ui') -// we want to do '[\p{L}--[a-zA-Z]]', but the 'v' flag breaks on Ubunto Node (weird) -export const tldNameREString = '(?:(?![a-zA-Z])\\p{L}|[a-zA-Z\\p{L}]{2,63})' +// note the 'v' flag breaks on Ubuntu +export const tldNameREString = `(?:(?![0-9])(?:[${uniNonASCII}]|[a-zA-Z${uniNonASCII}][a-zA-Z0-9${uniNonASCII}]{1,62}))` // Web: Matches a Top Level Domain (TLD). See [domain name rules](#domain-name-rules). When using the partial string to create a RE, you must use the 'u' or 'v' flag. export const tldNameRE = lockdownRE(tldNameREString, 'u') -export const subdomainLabelREString = '(?:(?![a-zA-Z])\\p{L}|[\\p{L}0-9]' + - '(?:[\\p{L}0-9]|' + // two letters only - '[\\p{L}0-9\\-](?!--)[\\p{L}0-9\\-]{0,60}[\\p{L}0-9]))' // otherwise, verify the 3rd and 4th positions are not '-' +export const subdomainLabelREString = `(?:[${uniNonASCII}]|[a-zA-Z0-9${uniNonASCII}]` + // allow single unicode + `(?:[a-zA-Z0-9${uniNonASCII}]|` + // two letters only + // otherwise, verify the 3rd and 4th positions are not '-' + `[a-zA-Z0-9${uniNonASCII}\\-](?!--)[a-zA-Z0-9${uniNonASCII}\\-]{0,60}[\\p{L}0-9]))` // Web: Matches a registerable domain name. Partially enforces the 63 byte domain label limit, but this is only valid for non-international (all ASCII) labels because we can only count characters. See [domain name rules](#domain-name-rules). When using the partial string to create a RE, you must use the 'u' or 'v' flag. export const subdomainLabelRE = lockdownRE(subdomainLabelREString, 'u') -export const fqDomainNameREString = `(?![0-9\\p{L}.\\-]{256,})(?:${subdomainLabelREString}\\.)+${tldNameREString}` +// export const fqDomainNameREString = `(?![0-9\\p{L}.\\-]{256,})(?:${subdomainLabelREString}\\.)+${tldNameREString}` +export const fqDomainNameREString = `(?!.{256,})(?:${subdomainLabelREString}\\.)+${tldNameREString}` // Web: Matches fully qualified domain name (one or more subdomains + TLD). Partially enforces the 255 byte FQ domain name limit, but this is only valid for non-international (all ASCII) domain names because we can only count characters. When using the partial string to create a RE, you must use the 'u' or 'v' flag. export const fqDomainNameRE = lockdownRE(fqDomainNameREString, 'u') - -// based on https://stackoverflow.com/a/201378/929494; modified to allow uppercase characters and restrict to valid DNS -// names in the domain portion -export const emailREString = `([a-zA-Z0-9!#$%&'*+\\/=?^_\`\\{\\|\\}~\\-]+(?:\\.[a-zA-Z0-9!#$%&'*+\\/=?^_\`\\{\\|\\}~\\-]+)*|"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*")@(${fqDomainNameREString})` -// Contact info: Match a valid email. Provides matching groups 1 (user name) and 2 (domain). When using the partial string to create a RE, you must use the 'u' flag. -export const emailRE = lockdownRE(emailREString, 'u')