From 9b3fa32a36448beaf111ac7bd076ac9f27891a3a Mon Sep 17 00:00:00 2001 From: Juan P Lopez Date: Fri, 24 Nov 2023 09:16:38 -0500 Subject: [PATCH] chore(core): add disposable phone validation (#3598) --- core/api/package.json | 1 + core/api/src/services/twilio-service.ts | 12 ++- .../twilio/is-disposable-phone-number.spec.ts | 16 +++ pnpm-lock.yaml | 98 +++++++++---------- 4 files changed, 74 insertions(+), 53 deletions(-) create mode 100644 core/api/test/unit/services/twilio/is-disposable-phone-number.spec.ts diff --git a/core/api/package.json b/core/api/package.json index 27f892f2b3..43cb1a784f 100644 --- a/core/api/package.json +++ b/core/api/package.json @@ -36,6 +36,7 @@ "@google-cloud/storage": "^7.6.0", "@grpc/grpc-js": "^1.9.10", "@grpc/proto-loader": "^0.7.10", + "@ip1sms/disposable-phone-numbers": "^2.1.600", "@opentelemetry/api": "^1.7.0", "@opentelemetry/core": "^1.18.1", "@opentelemetry/exporter-trace-otlp-http": "^0.45.1", diff --git a/core/api/src/services/twilio-service.ts b/core/api/src/services/twilio-service.ts index 519a58d60a..7dd336e1df 100644 --- a/core/api/src/services/twilio-service.ts +++ b/core/api/src/services/twilio-service.ts @@ -1,6 +1,6 @@ import twilio from "twilio" - import { isAxiosError } from "axios" +import disposablePhoneList from "@ip1sms/disposable-phone-numbers" import { wrapAsyncFunctionsToRunInSpan } from "./tracing" @@ -49,6 +49,10 @@ export const TwilioClient = (): IPhoneProviderService => { channel: ChannelType }): Promise => { try { + if (isDisposablePhoneNumber(to)) { + return new InvalidTypePhoneProviderError() + } + const lookup = await client.lookups.v2.phoneNumbers(to).fetch({ fields: "line_type_intelligence", }) @@ -56,6 +60,7 @@ export const TwilioClient = (): IPhoneProviderService => { if (lookup.lineTypeIntelligence.type === "nonFixedVoip") { return new InvalidTypePhoneProviderError() } + await verify.verifications.create({ to, channel }) } catch (err) { baseLogger.error({ err }, "impossible to send text") @@ -226,3 +231,8 @@ export const isPhoneCodeValid = async ({ return TwilioClient().validateVerify({ to: phone, code }) } + +export const isDisposablePhoneNumber = (phone: PhoneNumber) => { + const phoneNumber = phone.replace(/[^0-9]/, "") + return phoneNumber in disposablePhoneList +} diff --git a/core/api/test/unit/services/twilio/is-disposable-phone-number.spec.ts b/core/api/test/unit/services/twilio/is-disposable-phone-number.spec.ts new file mode 100644 index 0000000000..15405f307b --- /dev/null +++ b/core/api/test/unit/services/twilio/is-disposable-phone-number.spec.ts @@ -0,0 +1,16 @@ +import { isDisposablePhoneNumber } from "@/services/twilio-service" + +describe("Twilio Service", () => { + describe("isDisposablePhoneNumber", () => { + const amounts = [ + { phone: "+34689682259", expected: true }, + { phone: "34689682259", expected: true }, + { phone: "+34699682259", expected: false }, + { phone: "34699682259", expected: false }, + ] + test.each(amounts)("phone $phone is disposable: $expected", ({ phone, expected }) => { + const result = isDisposablePhoneNumber(phone as PhoneNumber) + expect(result).toEqual(expected) + }) + }) +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 095cc794f8..54bcd7c9c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -314,6 +314,9 @@ importers: '@grpc/proto-loader': specifier: ^0.7.10 version: 0.7.10 + '@ip1sms/disposable-phone-numbers': + specifier: ^2.1.600 + version: 2.1.600 '@opentelemetry/api': specifier: ^1.7.0 version: 1.7.0 @@ -1165,7 +1168,7 @@ packages: '@babel/traverse': 7.23.3 '@babel/types': 7.23.3 convert-source-map: 2.0.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -1253,7 +1256,7 @@ packages: '@babel/core': 7.23.3 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -2631,7 +2634,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.3 '@babel/types': 7.23.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -3080,7 +3083,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) espree: 9.6.1 globals: 13.23.0 ignore: 5.2.4 @@ -4638,7 +4641,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 2.0.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -4657,6 +4660,10 @@ packages: resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} dev: false + /@ip1sms/disposable-phone-numbers@2.1.600: + resolution: {integrity: sha512-9WOXVOJUqa5NuR2HdMkbxRitrNJT75eCUhigqHPWYlVyCDUTYQkmIavYplCRht9s6PXiaky6SmO0h1+xuUtmYg==} + dev: false + /@istanbuljs/load-nyc-config@1.1.0: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -7019,7 +7026,7 @@ packages: '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 graphemer: 1.4.0 ignore: 5.2.4 @@ -7066,7 +7073,7 @@ packages: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 typescript: 5.2.2 transitivePeerDependencies: @@ -7101,7 +7108,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 @@ -7135,7 +7142,7 @@ packages: dependencies: '@typescript-eslint/types': 4.33.0 '@typescript-eslint/visitor-keys': 4.33.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 @@ -7156,7 +7163,7 @@ packages: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 @@ -7177,7 +7184,7 @@ packages: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 @@ -7198,7 +7205,7 @@ packages: dependencies: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 @@ -7403,7 +7410,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -7411,7 +7418,7 @@ packages: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} engines: {node: '>= 14'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -9175,17 +9182,6 @@ packages: supports-color: 8.1.1 dev: true - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -9197,7 +9193,6 @@ packages: dependencies: ms: 2.1.2 supports-color: 8.1.1 - dev: true /decamelize@1.2.0: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} @@ -9389,7 +9384,7 @@ packages: hasBin: true dependencies: commander: 2.20.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) filing-cabinet: 3.3.1 precinct: 9.2.1 typescript: 4.9.5 @@ -9482,7 +9477,7 @@ packages: resolution: {integrity: sha512-Rps1xDkEEBSq3kLdsdnHZL1x2S4NGDcbrjmd4q+PykK5aJwDdP5MBgrJw1Xo+kyUHuv3JEzPqxr+Dj9ryeDRTA==} engines: {node: '>= 6.0'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) gonzales-pe: 4.3.0 node-source-walk: 4.3.0 transitivePeerDependencies: @@ -9493,7 +9488,7 @@ packages: resolution: {integrity: sha512-Fwc/g9VcrowODIAeKRWZfVA/EufxYL7XfuqJQFroBKGikKX83d2G7NFw6kDlSYGG3LNQIyVa+eWv1mqre+v4+A==} engines: {node: ^10 || ^12 || >=14} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) is-url: 1.2.4 postcss: 8.4.31 postcss-values-parser: 2.0.1 @@ -10027,7 +10022,7 @@ packages: /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) is-core-module: 2.13.1 resolve: 1.22.8 transitivePeerDependencies: @@ -10109,7 +10104,7 @@ packages: optional: true dependencies: '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.2.2) - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) eslint: 8.53.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: @@ -10131,7 +10126,7 @@ packages: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 eslint: 8.53.0 eslint-import-resolver-node: 0.3.9 @@ -10433,7 +10428,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -10929,7 +10924,7 @@ packages: dependencies: app-module-path: 2.2.0 commander: 2.20.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) enhanced-resolve: 5.15.0 is-relative-path: 1.0.2 module-definition: 3.4.0 @@ -12259,7 +12254,7 @@ packages: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: false @@ -12298,7 +12293,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -12307,7 +12302,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -12331,7 +12326,7 @@ packages: engines: {node: '>=10'} dependencies: '@messageformat/core': 3.2.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) fast-printf: 1.6.9 make-plural: 7.3.0 math-interval-parser: 2.0.1 @@ -12531,7 +12526,7 @@ packages: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -12991,7 +12986,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) istanbul-lib-coverage: 3.2.0 source-map: 0.6.1 transitivePeerDependencies: @@ -13798,7 +13793,7 @@ packages: dependencies: '@types/express': 4.17.21 '@types/jsonwebtoken': 9.0.5 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) jose: 4.15.2 limiter: 1.1.5 lru-memoizer: 2.2.0 @@ -13885,7 +13880,7 @@ packages: dependencies: colorette: 2.0.19 commander: 10.0.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) escalade: 3.1.1 esm: 3.2.25 get-package-type: 0.1.0 @@ -14319,7 +14314,7 @@ packages: chalk: 4.1.2 commander: 7.2.0 commondir: 1.0.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) dependency-tree: 9.0.0 detective-amd: 4.2.0 detective-cjs: 4.1.0 @@ -14663,7 +14658,7 @@ packages: hasBin: true dependencies: commander: 2.20.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) glob: 7.2.3 requirejs: 2.3.6 requirejs-config-file: 4.0.0 @@ -14783,7 +14778,7 @@ packages: resolution: {integrity: sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==} engines: {node: '>=14.0.0'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: false @@ -15912,7 +15907,7 @@ packages: hasBin: true dependencies: commander: 2.20.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) detective-amd: 3.1.2 detective-cjs: 3.1.3 detective-es6: 2.2.2 @@ -16552,7 +16547,7 @@ packages: resolution: {integrity: sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw==} engines: {node: '>=8.6.0'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: @@ -16674,7 +16669,7 @@ packages: engines: {node: '>=12'} requiresBuild: true dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) extend: 3.0.2 transitivePeerDependencies: - supports-color @@ -16686,7 +16681,7 @@ packages: engines: {node: '>=14'} dependencies: '@types/request': 2.48.10 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) extend: 3.0.2 teeny-request: 9.0.0 transitivePeerDependencies: @@ -17434,7 +17429,7 @@ packages: hasBin: true dependencies: commander: 2.20.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -17498,7 +17493,6 @@ packages: engines: {node: '>=10'} dependencies: has-flag: 4.0.0 - dev: true /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} @@ -17761,7 +17755,7 @@ packages: resolution: {integrity: sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==} dependencies: body: 5.1.0 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) faye-websocket: 0.10.0 livereload-js: 2.4.0 object-assign: 4.1.1