From dd2cb1649a870e44c63f5fe3554517cae2192412 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 19 May 2021 11:25:21 -0700 Subject: [PATCH 01/27] chore: update CHANGELOG --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 781b11c4..1a995883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,7 @@ VS Code v0.00.0 ### Documentation - docs: add Pomerium #3424 @desimone -- docs: fix confusing sentence in pull requests section #3460 @shiv-tyagi +- docs: fix confusing sentence in pull requests section #3460 @shiv-tyag - docs: remove toc from changelog @oxy @jsjoeio - docs(MAINTAINING): add information about CHANGELOG #3467 @jsjoeio - docs: move release process to MAINTAINING.md #3441 @oxy @Prashant168 @@ -59,6 +59,7 @@ VS Code v0.00.0 - chore: cross-compile docker images with buildx #3166 @oxy - chore: update node to v14 #3458 @oxy - chore: update .gitignore #3557 @cuining +- fix: use sufficient computational effort for password hash #3422 @jsjoeio ### Development @@ -81,6 +82,10 @@ VS Code v1.56.1 - fix(ci): update brew-bump.sh to update remote first #3438 @jsjoeio +### Documentation + +- item + ## 3.10.1 VS Code v1.56.1 From cac667317ef03020130aa57ab88480a7ded09bdb Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 19 May 2021 14:10:25 -0700 Subject: [PATCH 02/27] refactor: use bcrypt in hash function --- package.json | 2 + src/node/util.ts | 3 +- yarn.lock | 225 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 223 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index d274e00e..d90e62fe 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "main": "out/node/entry.js", "devDependencies": { "@schemastore/package": "^0.0.6", + "@types/bcrypt": "^5.0.0", "@types/body-parser": "^1.19.0", "@types/compression": "^1.7.0", "@types/cookie-parser": "^1.4.2", @@ -88,6 +89,7 @@ }, "dependencies": { "@coder/logger": "1.1.16", + "bcrypt": "^5.0.1", "body-parser": "^1.19.0", "compression": "^1.7.4", "cookie-parser": "^1.4.5", diff --git a/src/node/util.ts b/src/node/util.ts index f1882471..e456f809 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -1,5 +1,6 @@ import * as cp from "child_process" import * as crypto from "crypto" +import * as bcrypt from "bcrypt" import envPaths from "env-paths" import { promises as fs } from "fs" import * as net from "net" @@ -116,7 +117,7 @@ export const generatePassword = async (length = 24): Promise => { } export const hash = (str: string): string => { - return crypto.createHash("sha256").update(str).digest("hex") + return bcrypt.hashSync(str, 10) } const mimeTypes: { [key: string]: string } = { diff --git a/yarn.lock b/yarn.lock index 991ebe34..392110f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -896,6 +896,21 @@ resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== +"@mapbox/node-pre-gyp@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" + integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA== + dependencies: + detect-libc "^1.0.3" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.1" + nopt "^5.0.0" + npmlog "^4.1.2" + rimraf "^3.0.2" + semver "^7.3.4" + tar "^6.1.0" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -1039,6 +1054,13 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== +"@types/bcrypt@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-5.0.0.tgz#a835afa2882d165aff5690893db314eaa98b9f20" + integrity sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw== + dependencies: + "@types/node" "*" + "@types/body-parser@*", "@types/body-parser@^1.19.0": version "1.19.0" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" @@ -1317,6 +1339,11 @@ abab@^2.0.0: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -1397,6 +1424,11 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" @@ -1436,6 +1468,19 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -1713,6 +1758,14 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bcrypt@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.0.1.tgz#f1a2c20f208e2ccdceea4433df0c8b2c54ecdf71" + integrity sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.0" + node-addon-api "^3.1.0" + binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -2110,6 +2163,11 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -2175,6 +2233,11 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + codecov@^3.8.1: version "3.8.2" resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.8.2.tgz#ab24f18783998c39e809ea210af899f8dbcc790e" @@ -2316,6 +2379,11 @@ console-browserify@^1.1.0: resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -2848,6 +2916,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -2866,6 +2939,11 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -3730,6 +3808,13 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3761,6 +3846,20 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -3949,6 +4048,11 @@ has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -4438,6 +4542,18 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -4928,6 +5044,13 @@ magic-string@^0.22.4: dependencies: vlq "^0.2.2" +make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -5172,6 +5295,21 @@ minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -5192,6 +5330,11 @@ mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "^1.2.5" +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -5269,6 +5412,11 @@ node-addon-api@^1.7.1: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== +node-addon-api@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.0.tgz#7028b56a7eb572b73873aed731a7f9c9365f5ee4" + integrity sha512-kcwSAWhPi4+QzAtsL2+2s/awvDo2GKLsvMCwNRxb5BUshteXU8U97NCyvQDsGKs/m0He9WcG4YWew/BnuLx++w== + node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" @@ -5313,6 +5461,13 @@ node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0, normalize-package-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.1.tgz#98dc56dfe6755d99b1c53f046e1e3d2dde55a1c7" @@ -5355,6 +5510,16 @@ normalize-url@^4.5.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== +npmlog@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -5374,6 +5539,11 @@ num2fraction@^1.2.2: resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + nwsapi@^2.1.3: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" @@ -5384,7 +5554,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.1.1: +object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -6760,7 +6930,7 @@ readable-stream@1.1.x: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6: +readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -7158,12 +7328,12 @@ semver@^5.4.1, semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: +semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -7204,7 +7374,7 @@ serve-static@1.14.1, serve-static@^1.12.4: parseurl "~1.3.3" send "0.17.1" -set-blocking@^2.0.0: +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -7276,7 +7446,7 @@ shellcheck@^1.0.0: resolved "https://registry.yarnpkg.com/shellcheck/-/shellcheck-1.0.0.tgz#263479d92c3708d63d98883f896481461cf17cd0" integrity sha512-CdKbWXOknBwE1wNQzAnwfLf7QNOu/yqyLSGBKoq2WuChEqfg7dnZJ1pHR2P463PbVpBRz3KGkYnXJCoQrPwtYA== -signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== @@ -7559,6 +7729,23 @@ stream-http@^2.7.2: to-arraybuffer "^1.0.0" xtend "^4.0.0" +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" @@ -7603,6 +7790,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" @@ -7823,6 +8017,18 @@ tar-stream@^2.1.4, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" +tar@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + teeny-request@7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.0.1.tgz#bdd41fdffea5f8fbc0d29392cb47bec4f66b2b4c" @@ -8527,6 +8733,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" From 17be8c5cd31c2ee036aadeeaf4cc19823c6f832a Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 19 May 2021 14:10:52 -0700 Subject: [PATCH 03/27] refactor: use bcrypt in e2e setup --- test/config.ts | 4 +- test/package.json | 2 + test/yarn.lock | 266 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 262 insertions(+), 10 deletions(-) diff --git a/test/config.ts b/test/config.ts index 53b592b7..a4c230d3 100644 --- a/test/config.ts +++ b/test/config.ts @@ -8,7 +8,7 @@ import { Config, globalSetup, } from "@playwright/test" -import * as crypto from "crypto" +import * as bcrypt from "bcrypt" import path from "path" import { PASSWORD } from "./utils/constants" import * as wtfnode from "./utils/wtfnode" @@ -16,7 +16,7 @@ import * as wtfnode from "./utils/wtfnode" // Playwright doesn't like that ../src/node/util has an enum in it // so I had to copy hash in separately const hash = (str: string): string => { - return crypto.createHash("sha256").update(str).digest("hex") + return bcrypt.hashSync(str, 10) } const cookieToStore = { diff --git a/test/package.json b/test/package.json index 2c389c19..04caccd2 100644 --- a/test/package.json +++ b/test/package.json @@ -3,10 +3,12 @@ "#": "We must put jest in a sub-directory otherwise VS Code somehow picks up the types and generates conflicts with mocha.", "devDependencies": { "@playwright/test": "^0.1101.0-alpha2", + "@types/bcrypt": "^5.0.0", "@types/jest": "^26.0.20", "@types/jsdom": "^16.2.6", "@types/node-fetch": "^2.5.8", "@types/supertest": "^2.0.10", + "bcrypt": "^5.0.1", "jest": "^26.6.3", "jsdom": "^16.4.0", "node-fetch": "^2.6.1", diff --git a/test/yarn.lock b/test/yarn.lock index d02c5a76..f877c1e4 100644 --- a/test/yarn.lock +++ b/test/yarn.lock @@ -1258,6 +1258,21 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@mapbox/node-pre-gyp@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" + integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA== + dependencies: + detect-libc "^1.0.3" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.1" + nopt "^5.0.0" + npmlog "^4.1.2" + rimraf "^3.0.2" + semver "^7.3.4" + tar "^6.1.0" + "@playwright/test@^0.1101.0-alpha2": version "0.1101.0-alpha2" resolved "https://registry.yarnpkg.com/@playwright/test/-/test-0.1101.0-alpha2.tgz#a1121ac70e6ac986532ea63c6f565f06fae99364" @@ -1314,6 +1329,13 @@ dependencies: "@babel/types" "^7.3.0" +"@types/bcrypt@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-5.0.0.tgz#a835afa2882d165aff5690893db314eaa98b9f20" + integrity sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw== + dependencies: + "@types/node" "*" + "@types/cookiejar@*": version "2.1.2" resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" @@ -1439,6 +1461,11 @@ abab@^2.0.3: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -1481,6 +1508,16 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.11.0" +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" @@ -1516,6 +1553,19 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1697,6 +1747,14 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bcrypt@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.0.1.tgz#f1a2c20f208e2ccdceea4433df0c8b2c54ecdf71" + integrity sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.0" + node-addon-api "^3.1.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1845,6 +1903,11 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -1879,6 +1942,11 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -1948,6 +2016,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" @@ -1973,7 +2046,7 @@ core-js-compat@^3.9.0, core-js-compat@^3.9.1: browserslist "^4.16.3" semver "7.0.0" -core-util-is@1.0.2: +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -2104,6 +2177,16 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -2442,6 +2525,13 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -2476,6 +2566,20 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -2586,6 +2690,11 @@ has-symbols@^1.0.1: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -2691,7 +2800,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.0: +inherits@2, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2788,6 +2897,18 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -2849,7 +2970,7 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -isarray@1.0.0: +isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -3473,7 +3594,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -make-dir@^3.0.0: +make-dir@^3.0.0, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -3575,6 +3696,21 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -3583,7 +3719,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@1.x: +mkdirp@1.x, mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -3637,6 +3773,11 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-addon-api@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.0.tgz#7028b56a7eb572b73873aed731a7f9c9365f5ee4" + integrity sha512-kcwSAWhPi4+QzAtsL2+2s/awvDo2GKLsvMCwNRxb5BUshteXU8U97NCyvQDsGKs/m0He9WcG4YWew/BnuLx++w== + node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" @@ -3669,6 +3810,13 @@ node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -3705,6 +3853,21 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" +npmlog@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" @@ -3715,6 +3878,11 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -3938,6 +4106,11 @@ pretty-format@^26.0.0, pretty-format@^26.4.2, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -4017,6 +4190,19 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +readable-stream@^2.0.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -4216,7 +4402,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.1: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -4277,7 +4463,14 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -set-blocking@^2.0.0: +semver@^7.3.4: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -4486,6 +4679,23 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^4.1.0, string-width@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" @@ -4502,6 +4712,27 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -4576,6 +4807,18 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +tar@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -4785,7 +5028,7 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@^1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -4897,6 +5140,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" From f35120c0a346b2a85903fef35c78cab778846876 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 19 May 2021 14:11:08 -0700 Subject: [PATCH 04/27] feat: add unit test for hash function --- test/unit/node/util.test.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index bb1884b9..0b085f09 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -1,3 +1,5 @@ +import { hash } from "../../../src/node/util" + describe("getEnvPaths", () => { describe("on darwin", () => { let ORIGINAL_PLATFORM = "" @@ -145,3 +147,11 @@ describe("getEnvPaths", () => { }) }) }) + +describe("hash", () => { + it("should return a hash of the string passed in", () => { + const plainTextPassword = "mySecretPassword123" + const hashed = hash(plainTextPassword) + expect(hashed).not.toBe(plainTextPassword) + }) +}) From aaf044728f6db2cbbad44d453c98dfc957dd4571 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 19 May 2021 16:17:32 -0700 Subject: [PATCH 05/27] refactor: add functions to check hash password --- src/node/http.ts | 4 ++-- src/node/routes/login.ts | 7 ++++--- src/node/util.ts | 35 +++++++++++++++++++++++++++++++++-- test/unit/node/util.test.ts | 36 +++++++++++++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src/node/http.ts b/src/node/http.ts index eb8c91f9..e0309542 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -8,7 +8,7 @@ import { normalize, Options } from "../common/util" import { AuthType, DefaultedArgs } from "./cli" import { commit, rootPath } from "./constants" import { Heart } from "./heart" -import { hash } from "./util" +import { isHashMatch } from "./util" declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -67,7 +67,7 @@ export const authenticated = (req: express.Request): boolean => { req.cookies.key && (req.args["hashed-password"] ? safeCompare(req.cookies.key, req.args["hashed-password"]) - : req.args.password && safeCompare(req.cookies.key, hash(req.args.password))) + : req.args.password && isHashMatch(req.args.password, req.cookies.key)) ) default: throw new Error(`Unsupported auth type ${req.args.auth}`) diff --git a/src/node/routes/login.ts b/src/node/routes/login.ts index 017b8830..59444fd2 100644 --- a/src/node/routes/login.ts +++ b/src/node/routes/login.ts @@ -5,7 +5,7 @@ import * as path from "path" import safeCompare from "safe-compare" import { rootPath } from "../constants" import { authenticated, getCookieDomain, redirect, replaceTemplates } from "../http" -import { hash, humanPath } from "../util" +import { hash, hashLegacy, humanPath, isHashLegacyMatch } from "../util" export enum Cookie { Key = "key", @@ -74,12 +74,13 @@ router.post("/", async (req, res) => { if ( req.args["hashed-password"] - ? safeCompare(hash(req.body.password), req.args["hashed-password"]) + ? isHashLegacyMatch(req.body.password, req.args["hashed-password"]) : req.args.password && safeCompare(req.body.password, req.args.password) ) { + const hashedPassword = req.args["hashed-password"] ? hashLegacy(req.body.password) : hash(req.body.password) // The hash does not add any actual security but we do it for // obfuscation purposes (and as a side effect it handles escaping). - res.cookie(Cookie.Key, hash(req.body.password), { + res.cookie(Cookie.Key, hashedPassword, { domain: getCookieDomain(req.headers.host || "", req.args["proxy-domain"]), path: req.body.base || "/", sameSite: "lax", diff --git a/src/node/util.ts b/src/node/util.ts index e456f809..495c3389 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -8,6 +8,7 @@ import * as os from "os" import * as path from "path" import * as util from "util" import xdgBasedir from "xdg-basedir" +import safeCompare from "safe-compare" export interface Paths { data: string @@ -116,8 +117,38 @@ export const generatePassword = async (length = 24): Promise => { return buffer.toString("hex").substring(0, length) } -export const hash = (str: string): string => { - return bcrypt.hashSync(str, 10) +/** + * Used to hash the password. + */ +export const hash = (password: string): string => { + return bcrypt.hashSync(password, 10) +} + +/** + * Used to verify if the password matches the hash + */ +export const isHashMatch = (password: string, hash: string) => { + return bcrypt.compareSync(password, hash) +} + +/** + * Used to hash the password using the sha256 + * algorithm. We only use this to for checking + * the hashed-password set in the config. + * + * Kept for legacy reasons. + */ +export const hashLegacy = (str: string): string => { + return crypto.createHash("sha256").update(str).digest("hex") +} + +/** + * Used to check if the password matches the hash using + * the hashLegacy function + */ +export const isHashLegacyMatch = (password: string, hashPassword: string) => { + const hashedWithLegacy = hashLegacy(password) + return safeCompare(hashedWithLegacy, hashPassword) } const mimeTypes: { [key: string]: string } = { diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index 0b085f09..c95994c7 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -1,4 +1,4 @@ -import { hash } from "../../../src/node/util" +import { hash, isHashMatch, hashLegacy, isHashLegacyMatch } from "../../../src/node/util" describe("getEnvPaths", () => { describe("on darwin", () => { @@ -155,3 +155,37 @@ describe("hash", () => { expect(hashed).not.toBe(plainTextPassword) }) }) + +describe("isHashMatch", () => { + it("should return true if the password matches the hash", () => { + const password = "password123" + const _hash = hash(password) + expect(isHashMatch(password, _hash)).toBe(true) + }) + it("should return false if the password does not match the hash", () => { + const password = "password123" + const _hash = hash(password) + expect(isHashMatch("otherPassword123", _hash)).toBe(false) + }) +}) + +describe("hashLegacy", () => { + it("should return a hash of the string passed in", () => { + const plainTextPassword = "mySecretPassword123" + const hashed = hashLegacy(plainTextPassword) + expect(hashed).not.toBe(plainTextPassword) + }) +}) + +describe("isHashLegacyMatchh", () => { + it("should return true if is match", () => { + const password = "password123" + const _hash = hashLegacy(password) + expect(isHashLegacyMatch(password, _hash)).toBe(true) + }) + it("should return false if is match", () => { + const password = "password123" + const _hash = hashLegacy(password) + expect(isHashLegacyMatch("otherPassword123", _hash)).toBe(false) + }) +}) From fc3326f1f2ce6cd2f176e941a3615128639f3ce4 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Thu, 20 May 2021 16:26:19 -0700 Subject: [PATCH 06/27] feat: add tests using real hashes --- CHANGELOG.md | 6 +----- test/unit/node/util.test.ts | 13 ++++++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a995883..bc4194d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,7 @@ VS Code v0.00.0 ### Documentation - docs: add Pomerium #3424 @desimone -- docs: fix confusing sentence in pull requests section #3460 @shiv-tyag +- docs: fix confusing sentence in pull requests section #3460 @shiv-tyagi - docs: remove toc from changelog @oxy @jsjoeio - docs(MAINTAINING): add information about CHANGELOG #3467 @jsjoeio - docs: move release process to MAINTAINING.md #3441 @oxy @Prashant168 @@ -82,10 +82,6 @@ VS Code v1.56.1 - fix(ci): update brew-bump.sh to update remote first #3438 @jsjoeio -### Documentation - -- item - ## 3.10.1 VS Code v1.56.1 diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index c95994c7..2254a412 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -167,6 +167,11 @@ describe("isHashMatch", () => { const _hash = hash(password) expect(isHashMatch("otherPassword123", _hash)).toBe(false) }) + it("should return true with actual hash", () => { + const password = "password" + const _hash = "$2b$10$GA/eZnlboeV9eW8LnntPqe1dZE7tQ/./wCdc7NgJhMRB.xhaJfmG." + expect(isHashMatch(password, _hash)).toBe(true) + }) }) describe("hashLegacy", () => { @@ -177,7 +182,7 @@ describe("hashLegacy", () => { }) }) -describe("isHashLegacyMatchh", () => { +describe("isHashLegacyMatch", () => { it("should return true if is match", () => { const password = "password123" const _hash = hashLegacy(password) @@ -188,4 +193,10 @@ describe("isHashLegacyMatchh", () => { const _hash = hashLegacy(password) expect(isHashLegacyMatch("otherPassword123", _hash)).toBe(false) }) + it("should return true if hashed from command line", () => { + const password = "password123" + // Hashed using printf "password123" | sha256sum | cut -d' ' -f1 + const _hash = "ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f" + expect(isHashLegacyMatch(password, _hash)).toBe(true) + }) }) From dc2db5c62d006432ae4ad161b2308ee4afab0dd4 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 10:11:53 -0700 Subject: [PATCH 07/27] chore: add argon2 package --- package.json | 1 + yarn.lock | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d90e62fe..32faa1ad 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ }, "dependencies": { "@coder/logger": "1.1.16", + "argon2": "^0.28.0", "bcrypt": "^5.0.1", "body-parser": "^1.19.0", "compression": "^1.7.4", diff --git a/yarn.lock b/yarn.lock index 392110f4..c2071104 100644 --- a/yarn.lock +++ b/yarn.lock @@ -896,7 +896,7 @@ resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== -"@mapbox/node-pre-gyp@^1.0.0": +"@mapbox/node-pre-gyp@^1.0.0", "@mapbox/node-pre-gyp@^1.0.1": version "1.0.5" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA== @@ -986,6 +986,11 @@ "@parcel/utils" "^1.11.0" physical-cpu-count "^2.0.0" +"@phc/format@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4" + integrity sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ== + "@schemastore/package@^0.0.6": version "0.0.6" resolved "https://registry.yarnpkg.com/@schemastore/package/-/package-0.0.6.tgz#9a76713da1c7551293b7e72e4f387f802bfd5d81" @@ -1486,6 +1491,16 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== +argon2@^0.28.0: + version "0.28.0" + resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.28.0.tgz#b32ea7526457f5593dda51c5a8e542bde3ba2621" + integrity sha512-bFgoLJbfNVbceXO9h8wKytoqBWyDsROp0HVlhtpyXCwq/izvMJ7CKZdDQp8y0T4yKAQHKD1vuW0C1INdjYdRsw== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.1" + "@phc/format" "^1.0.0" + node-addon-api "^3.0.2" + opencollective-postinstall "^2.0.3" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -5412,6 +5427,11 @@ node-addon-api@^1.7.1: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== +node-addon-api@^3.0.2: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== + node-addon-api@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.0.tgz#7028b56a7eb572b73873aed731a7f9c9365f5ee4" @@ -5652,6 +5672,11 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +opencollective-postinstall@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + opn@^5.1.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" From 51f8341959c1a6066ff6755602b9ae43dfba257a Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 10:27:57 -0700 Subject: [PATCH 08/27] chore: update to argon2 in test --- test/package.json | 5 +++-- test/yarn.lock | 45 +++++++++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/test/package.json b/test/package.json index 04caccd2..773c043f 100644 --- a/test/package.json +++ b/test/package.json @@ -3,12 +3,11 @@ "#": "We must put jest in a sub-directory otherwise VS Code somehow picks up the types and generates conflicts with mocha.", "devDependencies": { "@playwright/test": "^0.1101.0-alpha2", - "@types/bcrypt": "^5.0.0", "@types/jest": "^26.0.20", "@types/jsdom": "^16.2.6", "@types/node-fetch": "^2.5.8", "@types/supertest": "^2.0.10", - "bcrypt": "^5.0.1", + "argon2": "^0.28.0", "jest": "^26.6.3", "jsdom": "^16.4.0", "node-fetch": "^2.6.1", @@ -18,5 +17,7 @@ }, "resolutions": { "@playwright/test/playwright": "^1.11.0-next-alpha-apr-13-2021" + }, + "dependencies": { } } diff --git a/test/yarn.lock b/test/yarn.lock index f877c1e4..45b93461 100644 --- a/test/yarn.lock +++ b/test/yarn.lock @@ -1258,7 +1258,7 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@mapbox/node-pre-gyp@^1.0.0": +"@mapbox/node-pre-gyp@^1.0.1": version "1.0.5" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA== @@ -1273,6 +1273,11 @@ semver "^7.3.4" tar "^6.1.0" +"@phc/format@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4" + integrity sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ== + "@playwright/test@^0.1101.0-alpha2": version "0.1101.0-alpha2" resolved "https://registry.yarnpkg.com/@playwright/test/-/test-0.1101.0-alpha2.tgz#a1121ac70e6ac986532ea63c6f565f06fae99364" @@ -1329,13 +1334,6 @@ dependencies: "@babel/types" "^7.3.0" -"@types/bcrypt@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-5.0.0.tgz#a835afa2882d165aff5690893db314eaa98b9f20" - integrity sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw== - dependencies: - "@types/node" "*" - "@types/cookiejar@*": version "2.1.2" resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" @@ -1566,6 +1564,16 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +argon2@^0.28.0: + version "0.28.0" + resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.28.0.tgz#b32ea7526457f5593dda51c5a8e542bde3ba2621" + integrity sha512-bFgoLJbfNVbceXO9h8wKytoqBWyDsROp0HVlhtpyXCwq/izvMJ7CKZdDQp8y0T4yKAQHKD1vuW0C1INdjYdRsw== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.1" + "@phc/format" "^1.0.0" + node-addon-api "^3.0.2" + opencollective-postinstall "^2.0.3" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1747,14 +1755,6 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -bcrypt@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.0.1.tgz#f1a2c20f208e2ccdceea4433df0c8b2c54ecdf71" - integrity sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.0" - node-addon-api "^3.1.0" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3773,10 +3773,10 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-addon-api@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.0.tgz#7028b56a7eb572b73873aed731a7f9c9365f5ee4" - integrity sha512-kcwSAWhPi4+QzAtsL2+2s/awvDo2GKLsvMCwNRxb5BUshteXU8U97NCyvQDsGKs/m0He9WcG4YWew/BnuLx++w== +node-addon-api@^3.0.2: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== node-fetch@^2.6.1: version "2.6.1" @@ -3935,6 +3935,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +opencollective-postinstall@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" From 70197bb2a514cd8dfc1115233c94b385aed9afa7 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 12:47:22 -0700 Subject: [PATCH 09/27] refactor: use argon2 instead of bcrypt This uses argon2 instead of bcrypt. Note: this means the hash functions are now async which means we have to refactor a lot of other code around auth. --- src/node/util.ts | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/node/util.ts b/src/node/util.ts index 495c3389..e3039d89 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -1,6 +1,6 @@ import * as cp from "child_process" import * as crypto from "crypto" -import * as bcrypt from "bcrypt" +import * as argon2 from "argon2" import envPaths from "env-paths" import { promises as fs } from "fs" import * as net from "net" @@ -9,6 +9,7 @@ import * as path from "path" import * as util from "util" import xdgBasedir from "xdg-basedir" import safeCompare from "safe-compare" +import { logger } from "@coder/logger" export interface Paths { data: string @@ -120,15 +121,25 @@ export const generatePassword = async (length = 24): Promise => { /** * Used to hash the password. */ -export const hash = (password: string): string => { - return bcrypt.hashSync(password, 10) +export const hash = async (password: string): Promise => { + try { + return await argon2.hash(password) + } catch (error) { + logger.error(error) + return "" + } } /** * Used to verify if the password matches the hash */ -export const isHashMatch = (password: string, hash: string) => { - return bcrypt.compareSync(password, hash) +export const isHashMatch = async (password: string, hash: string) => { + try { + return await argon2.verify(hash, password) + } catch (error) { + logger.error(error) + return false + } } /** From fd3cb6cfa028b63c0e3600ce1283f8316cfad15a Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 13:01:01 -0700 Subject: [PATCH 10/27] refactor: update unit tests for hash fns Since the hash and isHashMatch are now async, I had to update the tests accordingly. Now everything is working. --- test/unit/node/util.test.ts | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index 2254a412..62d27ff2 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -149,28 +149,31 @@ describe("getEnvPaths", () => { }) describe("hash", () => { - it("should return a hash of the string passed in", () => { + it("should return a hash of the string passed in", async () => { const plainTextPassword = "mySecretPassword123" - const hashed = hash(plainTextPassword) + const hashed = await hash(plainTextPassword) expect(hashed).not.toBe(plainTextPassword) }) }) describe("isHashMatch", () => { - it("should return true if the password matches the hash", () => { - const password = "password123" - const _hash = hash(password) - expect(isHashMatch(password, _hash)).toBe(true) + it("should return true if the password matches the hash", async () => { + const password = "codeserver1234" + const _hash = await hash(password) + const actual = await isHashMatch(password, _hash) + expect(actual).toBe(true) }) - it("should return false if the password does not match the hash", () => { + it("should return false if the password does not match the hash", async () => { const password = "password123" - const _hash = hash(password) - expect(isHashMatch("otherPassword123", _hash)).toBe(false) + const _hash = await hash(password) + const actual = await isHashMatch("otherPassword123", _hash) + expect(actual).toBe(false) }) - it("should return true with actual hash", () => { - const password = "password" - const _hash = "$2b$10$GA/eZnlboeV9eW8LnntPqe1dZE7tQ/./wCdc7NgJhMRB.xhaJfmG." - expect(isHashMatch(password, _hash)).toBe(true) + it("should return true with actual hash", async () => { + const password = "password123" + const _hash = "$argon2i$v=19$m=4096,t=3,p=1$EAoczTxVki21JDfIZpTUxg$rkXgyrW4RDGoDYrxBFD4H2DlSMEhP4h+Api1hXnGnFY" + const actual = await isHashMatch(password, _hash) + expect(actual).toBe(true) }) }) From fcc3f0d951954159c711f79e145b6c0f973b8370 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 13:11:01 -0700 Subject: [PATCH 11/27] refactor: update login logic with new async hashing This adds the proper await logic for the hashing of passwords. --- src/node/routes/login.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/node/routes/login.ts b/src/node/routes/login.ts index 59444fd2..ecbc1804 100644 --- a/src/node/routes/login.ts +++ b/src/node/routes/login.ts @@ -77,7 +77,12 @@ router.post("/", async (req, res) => { ? isHashLegacyMatch(req.body.password, req.args["hashed-password"]) : req.args.password && safeCompare(req.body.password, req.args.password) ) { - const hashedPassword = req.args["hashed-password"] ? hashLegacy(req.body.password) : hash(req.body.password) + // NOTE@jsjoeio: + // We store the hashed password as a cookie. In order to be backwards-comptabile for the folks + // using sha256 (the original hashing algorithm), we need to check the hashed-password in the req.args + // TODO all of this logic should be cleaned up honestly. The current implementation only checks for a hashed-password + // but doesn't check which algorithm they are using. + const hashedPassword = req.args["hashed-password"] ? hashLegacy(req.body.password) : await hash(req.body.password) // The hash does not add any actual security but we do it for // obfuscation purposes (and as a side effect it handles escaping). res.cookie(Cookie.Key, hashedPassword, { From 0cdbd33b46e8e0446450fe0d5121a75b53242e70 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 13:21:59 -0700 Subject: [PATCH 12/27] refactor: make authenticated async everywhere Since this checks if they are authenticated using the hash/password and it's async, we need to update authenticated to be async, which means we have to update it everywhere it's used. --- src/node/http.ts | 15 +++++++++++---- src/node/routes/domainProxy.ts | 5 +++-- src/node/routes/login.ts | 4 ++-- src/node/routes/static.ts | 3 ++- src/node/routes/vscode.ts | 3 ++- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/node/http.ts b/src/node/http.ts index e0309542..5160d17f 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -45,8 +45,13 @@ export const replaceTemplates = ( /** * Throw an error if not authorized. Call `next` if provided. */ -export const ensureAuthenticated = (req: express.Request, _?: express.Response, next?: express.NextFunction): void => { - if (!authenticated(req)) { +export const ensureAuthenticated = async ( + req: express.Request, + _?: express.Response, + next?: express.NextFunction, +): Promise => { + const isAuthenticated = await authenticated(req) + if (!isAuthenticated) { throw new HttpError("Unauthorized", HttpCode.Unauthorized) } if (next) { @@ -57,17 +62,19 @@ export const ensureAuthenticated = (req: express.Request, _?: express.Response, /** * Return true if authenticated via cookies. */ -export const authenticated = (req: express.Request): boolean => { +export const authenticated = async (req: express.Request): Promise => { switch (req.args.auth) { case AuthType.None: return true case AuthType.Password: // The password is stored in the cookie after being hashed. + // TODO@jsjoeio this also needs to be refactored to check if they're using the legacy password + // or the new one. we can't assume hashed-password means legacy return !!( req.cookies.key && (req.args["hashed-password"] ? safeCompare(req.cookies.key, req.args["hashed-password"]) - : req.args.password && isHashMatch(req.args.password, req.cookies.key)) + : req.args.password && (await isHashMatch(req.args.password, req.cookies.key))) ) default: throw new Error(`Unsupported auth type ${req.args.auth}`) diff --git a/src/node/routes/domainProxy.ts b/src/node/routes/domainProxy.ts index 6b527255..26a3fa6f 100644 --- a/src/node/routes/domainProxy.ts +++ b/src/node/routes/domainProxy.ts @@ -32,14 +32,15 @@ const maybeProxy = (req: Request): string | undefined => { return port } -router.all("*", (req, res, next) => { +router.all("*", async (req, res, next) => { const port = maybeProxy(req) if (!port) { return next() } // Must be authenticated to use the proxy. - if (!authenticated(req)) { + const isAuthenticated = await authenticated(req) + if (!isAuthenticated) { // Let the assets through since they're used on the login page. if (req.path.startsWith("/static/") && req.method === "GET") { return next() diff --git a/src/node/routes/login.ts b/src/node/routes/login.ts index ecbc1804..4d0420eb 100644 --- a/src/node/routes/login.ts +++ b/src/node/routes/login.ts @@ -49,9 +49,9 @@ const limiter = new RateLimiter() export const router = Router() -router.use((req, res, next) => { +router.use(async (req, res, next) => { const to = (typeof req.query.to === "string" && req.query.to) || "/" - if (authenticated(req)) { + if (await authenticated(req)) { return redirect(req, res, to, { to: undefined }) } next() diff --git a/src/node/routes/static.ts b/src/node/routes/static.ts index 30eed031..5ba6983e 100644 --- a/src/node/routes/static.ts +++ b/src/node/routes/static.ts @@ -43,7 +43,8 @@ router.get("/(:commit)(/*)?", async (req, res) => { // Make sure it's in code-server if you aren't authenticated. This lets // unauthenticated users load the login assets. - if (!resourcePath.startsWith(rootPath) && !authenticated(req)) { + const isAuthenticated = await authenticated(req) + if (!resourcePath.startsWith(rootPath) && !isAuthenticated) { throw new HttpError("Unauthorized", HttpCode.Unauthorized) } diff --git a/src/node/routes/vscode.ts b/src/node/routes/vscode.ts index 40c63e92..7e59d52c 100644 --- a/src/node/routes/vscode.ts +++ b/src/node/routes/vscode.ts @@ -19,7 +19,8 @@ export const router = Router() const vscode = new VscodeProvider() router.get("/", async (req, res) => { - if (!authenticated(req)) { + const isAuthenticated = await authenticated(req) + if (!isAuthenticated) { return redirect(req, res, "login", { // req.baseUrl can be blank if already at the root. to: req.baseUrl && req.baseUrl !== "/" ? req.baseUrl : undefined, From 91303d4e40465a82f9652210992fd48b7e89b27d Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 13:27:55 -0700 Subject: [PATCH 13/27] refactor: make ensureAuthenticated async --- src/node/routes/domainProxy.ts | 4 ++-- src/node/routes/pathProxy.ts | 6 +++--- src/node/routes/static.ts | 2 +- typings/pluginapi.d.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/node/routes/domainProxy.ts b/src/node/routes/domainProxy.ts index 26a3fa6f..56b0ea1b 100644 --- a/src/node/routes/domainProxy.ts +++ b/src/node/routes/domainProxy.ts @@ -74,14 +74,14 @@ router.all("*", async (req, res, next) => { export const wsRouter = WsRouter() -wsRouter.ws("*", (req, _, next) => { +wsRouter.ws("*", async (req, _, next) => { const port = maybeProxy(req) if (!port) { return next() } // Must be authenticated to use the proxy. - ensureAuthenticated(req) + await ensureAuthenticated(req) proxy.ws(req, req.ws, req.head, { ignorePath: true, diff --git a/src/node/routes/pathProxy.ts b/src/node/routes/pathProxy.ts index 789fa5c1..e3200174 100644 --- a/src/node/routes/pathProxy.ts +++ b/src/node/routes/pathProxy.ts @@ -45,13 +45,13 @@ export function proxy( }) } -export function wsProxy( +export async function wsProxy( req: pluginapi.WebsocketRequest, opts?: { passthroughPath?: boolean }, -): void { - ensureAuthenticated(req) +): Promise { + await ensureAuthenticated(req) _proxy.ws(req, req.ws, req.head, { ignorePath: true, target: getProxyTarget(req, opts?.passthroughPath), diff --git a/src/node/routes/static.ts b/src/node/routes/static.ts index 5ba6983e..29a1ad3b 100644 --- a/src/node/routes/static.ts +++ b/src/node/routes/static.ts @@ -18,7 +18,7 @@ router.get("/(:commit)(/*)?", async (req, res) => { // Used by VS Code to load extensions into the web worker. const tar = getFirstString(req.query.tar) if (tar) { - ensureAuthenticated(req) + await ensureAuthenticated(req) let stream: Readable = tarFs.pack(pathToFsPath(tar)) if (req.headers["accept-encoding"] && req.headers["accept-encoding"].includes("gzip")) { logger.debug("gzipping tar", field("path", tar)) diff --git a/typings/pluginapi.d.ts b/typings/pluginapi.d.ts index f7797b6d..881aa1fb 100644 --- a/typings/pluginapi.d.ts +++ b/typings/pluginapi.d.ts @@ -145,12 +145,12 @@ export const proxy: ProxyServer /** * Middleware to ensure the user is authenticated. Throws if they are not. */ -export function ensureAuthenticated(req: express.Request, res?: express.Response, next?: express.NextFunction): void +export function ensureAuthenticated(req: express.Request, res?: express.Response, next?: express.NextFunction): Promise /** * Returns true if the user is authenticated. */ -export function authenticated(req: express.Request): boolean +export function authenticated(req: express.Request): Promise /** * Replace variables in HTML: TO, BASE, CS_STATIC_BASE, and OPTIONS. From 1134780b8b89c96aa99a621008f06cafee8deda4 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 13:29:20 -0700 Subject: [PATCH 14/27] refactor: make wsProxy async --- src/node/routes/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node/routes/index.ts b/src/node/routes/index.ts index c183b42b..42edbe11 100644 --- a/src/node/routes/index.ts +++ b/src/node/routes/index.ts @@ -98,8 +98,8 @@ export const register = async ( app.all("/proxy/(:port)(/*)?", (req, res) => { pathProxy.proxy(req, res) }) - wsApp.get("/proxy/(:port)(/*)?", (req) => { - pathProxy.wsProxy(req as pluginapi.WebsocketRequest) + wsApp.get("/proxy/(:port)(/*)?", async (req) => { + await pathProxy.wsProxy(req as pluginapi.WebsocketRequest) }) // These two routes pass through the path directly. // So the proxied app must be aware it is running @@ -109,8 +109,8 @@ export const register = async ( passthroughPath: true, }) }) - wsApp.get("/absproxy/(:port)(/*)?", (req) => { - pathProxy.wsProxy(req as pluginapi.WebsocketRequest, { + wsApp.get("/absproxy/(:port)(/*)?", async (req) => { + await pathProxy.wsProxy(req as pluginapi.WebsocketRequest, { passthroughPath: true, }) }) From 788b958e200708348f575e5c8076a367c1133f6f Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 13:31:50 -0700 Subject: [PATCH 15/27] refactor: update hash fn in test config --- test/config.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/config.ts b/test/config.ts index a4c230d3..d8e11db5 100644 --- a/test/config.ts +++ b/test/config.ts @@ -8,21 +8,21 @@ import { Config, globalSetup, } from "@playwright/test" -import * as bcrypt from "bcrypt" +import * as argon2 from "argon2" import path from "path" import { PASSWORD } from "./utils/constants" import * as wtfnode from "./utils/wtfnode" // Playwright doesn't like that ../src/node/util has an enum in it // so I had to copy hash in separately -const hash = (str: string): string => { - return bcrypt.hashSync(str, 10) +const hash = async (str: string): Promise => { + return await argon2.hash(str) } const cookieToStore = { sameSite: "Lax" as const, name: "key", - value: hash(PASSWORD), + value: "", domain: "localhost", path: "/", expires: -1, @@ -38,6 +38,8 @@ globalSetup(async () => { wtfnode.setup() } + cookieToStore.value = await hash(PASSWORD) + const storage = { cookies: [cookieToStore], } From ffa5c16e51a5e1096d8e0a530d97edd12d8f8592 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 14:18:54 -0700 Subject: [PATCH 16/27] feat: update cli and test for hashed-password --- src/node/cli.ts | 2 +- src/node/routes/login.ts | 11 ++++++++++- test/unit/cli.test.ts | 8 +++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 8278dfed..39d38fb3 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -114,7 +114,7 @@ const options: Options> = { "hashed-password": { type: "string", description: - "The password hashed with SHA-256 for password authentication (can only be passed in via $HASHED_PASSWORD or the config file). \n" + + "The password hashed with argon2 for password authentication (can only be passed in via $HASHED_PASSWORD or the config file). \n" + "Takes precedence over 'password'.", }, cert: { diff --git a/src/node/routes/login.ts b/src/node/routes/login.ts index 4d0420eb..eb9a775a 100644 --- a/src/node/routes/login.ts +++ b/src/node/routes/login.ts @@ -5,7 +5,7 @@ import * as path from "path" import safeCompare from "safe-compare" import { rootPath } from "../constants" import { authenticated, getCookieDomain, redirect, replaceTemplates } from "../http" -import { hash, hashLegacy, humanPath, isHashLegacyMatch } from "../util" +import { hash, hashLegacy, humanPath, isHashLegacyMatch, isHashMatch } from "../util" export enum Cookie { Key = "key", @@ -72,6 +72,14 @@ router.post("/", async (req, res) => { throw new Error("Missing password") } + // this logic below is flawed + const theHash = await hash(req.body.password) + const hashedPassword = req.args["hashed-password"] || "" + const match = await isHashMatch(req.body.password, hashedPassword) + // console.log(`The actual hash: ${theHash}`) + // console.log(`hashed-password from config: ${hashedPassword}`) + // console.log(theHash, hashedPassword) + console.log(`is it a match??? ${match}`) if ( req.args["hashed-password"] ? isHashLegacyMatch(req.body.password, req.args["hashed-password"]) @@ -82,6 +90,7 @@ router.post("/", async (req, res) => { // using sha256 (the original hashing algorithm), we need to check the hashed-password in the req.args // TODO all of this logic should be cleaned up honestly. The current implementation only checks for a hashed-password // but doesn't check which algorithm they are using. + console.log(`What is this? ${req.args["hashed-password"]}`, Boolean(req.args["hashed-password"])) const hashedPassword = req.args["hashed-password"] ? hashLegacy(req.body.password) : await hash(req.body.password) // The hash does not add any actual security but we do it for // obfuscation purposes (and as a side effect it handles escaping). diff --git a/test/unit/cli.test.ts b/test/unit/cli.test.ts index 6d4fcafb..340b1d79 100644 --- a/test/unit/cli.test.ts +++ b/test/unit/cli.test.ts @@ -305,8 +305,9 @@ describe("parser", () => { }) }) - it("should use env var hashed password", async () => { - process.env.HASHED_PASSWORD = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" // test + it.only("should use env var hashed password", async () => { + process.env.HASHED_PASSWORD = + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY" // test const args = parse([]) expect(args).toEqual({ _: [], @@ -316,7 +317,8 @@ describe("parser", () => { expect(defaultArgs).toEqual({ ...defaults, _: [], - "hashed-password": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + "hashed-password": + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", usingEnvHashedPassword: true, }) }) From 7ff4117531fb2f8030e5f0e6d78fdb8f7dd2f7b0 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 15:17:39 -0700 Subject: [PATCH 17/27] feat: add getPasswordMethod & test for it --- src/node/util.ts | 27 +++++++++++++++++++++++++++ test/unit/node/util.test.ts | 31 ++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/node/util.ts b/src/node/util.ts index e3039d89..551ed4b6 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -162,6 +162,33 @@ export const isHashLegacyMatch = (password: string, hashPassword: string) => { return safeCompare(hashedWithLegacy, hashPassword) } +const passwordMethods = ["SHA256", "ARGON2", "PLAIN_TEXT"] as const +export type PasswordMethod = typeof passwordMethods[number] + +/** + * Used to determine the password method. + * + * There are three options for the return value: + * 1. "SHA256" -> the legacy hashing algorithm + * 2. "ARGON2" -> the newest hashing algorithm + * 3. "PLAIN_TEXT" -> regular ol' password with no hashing + * + * @returns {PasswordMethod} "SHA256" | "ARGON2" | "PLAIN_TEXT" + */ +export function getPasswordMethod(hashedPassword: string | undefined): PasswordMethod { + if (!hashedPassword) { + return "PLAIN_TEXT" + } + + // This is the new hashing algorithm + if (hashedPassword.includes("$argon")) { + return "ARGON2" + } + + // This is the legacy hashing algorithm + return "SHA256" +} + const mimeTypes: { [key: string]: string } = { ".aac": "audio/x-aac", ".avi": "video/x-msvideo", diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index 62d27ff2..11fb701a 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -1,4 +1,11 @@ -import { hash, isHashMatch, hashLegacy, isHashLegacyMatch } from "../../../src/node/util" +import { + hash, + isHashMatch, + PasswordMethod, + getPasswordMethod, + hashLegacy, + isHashLegacyMatch, +} from "../../../src/node/util" describe("getEnvPaths", () => { describe("on darwin", () => { @@ -203,3 +210,25 @@ describe("isHashLegacyMatch", () => { expect(isHashLegacyMatch(password, _hash)).toBe(true) }) }) + +describe("getPasswordMethod", () => { + it("should return PLAIN_TEXT for no hashed password", () => { + const hashedPassword = undefined + const passwordMethod = getPasswordMethod(hashedPassword) + const expected: PasswordMethod = "PLAIN_TEXT" + expect(passwordMethod).toEqual(expected) + }) + it("should return ARGON2 for password with 'argon2'", () => { + const hashedPassword = + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY" + const passwordMethod = getPasswordMethod(hashedPassword) + const expected: PasswordMethod = "ARGON2" + expect(passwordMethod).toEqual(expected) + }) + it("should return SHA256 for password with legacy hash", () => { + const hashedPassword = "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af" + const passwordMethod = getPasswordMethod(hashedPassword) + const expected: PasswordMethod = "SHA256" + expect(passwordMethod).toEqual(expected) + }) +}) From a14ea39c4af09e322b0cd75c78068422919bb557 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 16:09:46 -0700 Subject: [PATCH 18/27] feat: add handlePasswordValidation + tests --- src/node/util.ts | 60 +++++++++++++++++++++++++ test/unit/node/util.test.ts | 90 +++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/src/node/util.ts b/src/node/util.ts index 551ed4b6..93d82851 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -189,6 +189,66 @@ export function getPasswordMethod(hashedPassword: string | undefined): PasswordM return "SHA256" } +type PasswordValidation = { + isPasswordValid: boolean + hashedPassword: string +} + +type HandlePasswordValidationArgs = { + /** The PasswordMethod */ + passwordMethod: PasswordMethod + /** The password provided by the user */ + passwordFromRequestBody: string + /** The password set in PASSWORD or config */ + passwordFromArgs: string | undefined + /** The hashed-password set in HASHED_PASSWORD or config */ + hashedPasswordFromArgs: string | undefined +} + +/** + * Checks if a password is valid and also returns the hash + * using the PasswordMethod + */ +export async function handlePasswordValidation( + passwordValidationArgs: HandlePasswordValidationArgs, +): Promise { + const { passwordMethod, passwordFromArgs, passwordFromRequestBody, hashedPasswordFromArgs } = passwordValidationArgs + // TODO implement + const passwordValidation = { + isPasswordValid: false, + hashedPassword: "", + } + + switch (passwordMethod) { + case "PLAIN_TEXT": { + const isValid = passwordFromArgs ? safeCompare(passwordFromRequestBody, passwordFromArgs) : false + passwordValidation.isPasswordValid = isValid + + const hashedPassword = await hash(passwordFromRequestBody) + passwordValidation.hashedPassword = hashedPassword + break + } + case "SHA256": { + const isValid = isHashLegacyMatch(passwordFromRequestBody, hashedPasswordFromArgs || "") + passwordValidation.isPasswordValid = isValid + + passwordValidation.hashedPassword = hashedPasswordFromArgs || (await hashLegacy(passwordFromRequestBody)) + break + } + case "ARGON2": { + const isValid = await isHashMatch(passwordFromRequestBody, hashedPasswordFromArgs || "") + passwordValidation.isPasswordValid = isValid + + passwordValidation.hashedPassword = hashedPasswordFromArgs || "" + break + } + default: + break + } + + return passwordValidation +} + const mimeTypes: { [key: string]: string } = { ".aac": "audio/x-aac", ".avi": "video/x-msvideo", diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index 11fb701a..5927ca32 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -1,6 +1,7 @@ import { hash, isHashMatch, + handlePasswordValidation, PasswordMethod, getPasswordMethod, hashLegacy, @@ -232,3 +233,92 @@ describe("getPasswordMethod", () => { expect(passwordMethod).toEqual(expected) }) }) + +describe.only("handlePasswordValidation", () => { + it("should return true with a hashedPassword for a PLAIN_TEXT password", async () => { + const p = "password" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "PLAIN_TEXT", + passwordFromRequestBody: p, + passwordFromArgs: p, + hashedPasswordFromArgs: undefined, + }) + + const matchesHash = await isHashMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(true) + expect(matchesHash).toBe(true) + }) + it("should return false when PLAIN_TEXT password doesn't match args", async () => { + const p = "password" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "PLAIN_TEXT", + passwordFromRequestBody: "password1", + passwordFromArgs: p, + hashedPasswordFromArgs: undefined, + }) + + const matchesHash = await isHashMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(false) + expect(matchesHash).toBe(false) + }) + it("should return true with a hashedPassword for a SHA256 password", async () => { + const p = "helloworld" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "SHA256", + passwordFromRequestBody: p, + passwordFromArgs: undefined, + hashedPasswordFromArgs: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af", + }) + + const matchesHash = isHashLegacyMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(true) + expect(matchesHash).toBe(true) + }) + it("should return false when SHA256 password doesn't match hash", async () => { + const p = "helloworld1" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "SHA256", + passwordFromRequestBody: p, + passwordFromArgs: undefined, + hashedPasswordFromArgs: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af", + }) + + const matchesHash = isHashLegacyMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(false) + expect(matchesHash).toBe(false) + }) + it("should return true with a hashedPassword for a ARGON2 password", async () => { + const p = "password" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "ARGON2", + passwordFromRequestBody: p, + passwordFromArgs: undefined, + hashedPasswordFromArgs: + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + }) + + const matchesHash = await isHashMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(true) + expect(matchesHash).toBe(true) + }) + it("should return false when ARGON2 password doesn't match hash", async () => { + const p = "password1" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "ARGON2", + passwordFromRequestBody: p, + passwordFromArgs: undefined, + hashedPasswordFromArgs: + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + }) + + const matchesHash = await isHashMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(false) + expect(matchesHash).toBe(false) + }) +}) From 409b473c82e12cebc59bf6b1e34b1f432c46d5e6 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 16:10:07 -0700 Subject: [PATCH 19/27] refactor: rewrite password logic at /login --- src/node/routes/login.ts | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/node/routes/login.ts b/src/node/routes/login.ts index eb9a775a..b09585ca 100644 --- a/src/node/routes/login.ts +++ b/src/node/routes/login.ts @@ -5,7 +5,15 @@ import * as path from "path" import safeCompare from "safe-compare" import { rootPath } from "../constants" import { authenticated, getCookieDomain, redirect, replaceTemplates } from "../http" -import { hash, hashLegacy, humanPath, isHashLegacyMatch, isHashMatch } from "../util" +import { + getPasswordMethod, + handlePasswordValidation, + hash, + hashLegacy, + humanPath, + isHashLegacyMatch, + isHashMatch, +} from "../util" export enum Cookie { Key = "key", @@ -62,36 +70,28 @@ router.get("/", async (req, res) => { }) router.post("/", async (req, res) => { + const password = req.body.password + const hashedPasswordFromArgs = req.args["hashed-password"] + try { // Check to see if they exceeded their login attempts if (!limiter.canTry()) { throw new Error("Login rate limited!") } - if (!req.body.password) { + if (!password) { throw new Error("Missing password") } - // this logic below is flawed - const theHash = await hash(req.body.password) - const hashedPassword = req.args["hashed-password"] || "" - const match = await isHashMatch(req.body.password, hashedPassword) - // console.log(`The actual hash: ${theHash}`) - // console.log(`hashed-password from config: ${hashedPassword}`) - // console.log(theHash, hashedPassword) - console.log(`is it a match??? ${match}`) - if ( - req.args["hashed-password"] - ? isHashLegacyMatch(req.body.password, req.args["hashed-password"]) - : req.args.password && safeCompare(req.body.password, req.args.password) - ) { - // NOTE@jsjoeio: - // We store the hashed password as a cookie. In order to be backwards-comptabile for the folks - // using sha256 (the original hashing algorithm), we need to check the hashed-password in the req.args - // TODO all of this logic should be cleaned up honestly. The current implementation only checks for a hashed-password - // but doesn't check which algorithm they are using. - console.log(`What is this? ${req.args["hashed-password"]}`, Boolean(req.args["hashed-password"])) - const hashedPassword = req.args["hashed-password"] ? hashLegacy(req.body.password) : await hash(req.body.password) + const passwordMethod = getPasswordMethod(hashedPasswordFromArgs) + const { isPasswordValid, hashedPassword } = await handlePasswordValidation({ + passwordMethod, + hashedPasswordFromArgs, + passwordFromRequestBody: password, + passwordFromArgs: req.args.password, + }) + + if (isPasswordValid) { // The hash does not add any actual security but we do it for // obfuscation purposes (and as a side effect it handles escaping). res.cookie(Cookie.Key, hashedPassword, { From 6020480b308d359f6f6ee0b55038f6ef9e1bc673 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 17:23:57 -0700 Subject: [PATCH 20/27] feat: add isCookieValid function and tests --- src/node/util.ts | 25 +++++++++++++++ test/unit/node/util.test.ts | 62 ++++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/node/util.ts b/src/node/util.ts index 93d82851..f575eb52 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -249,6 +249,31 @@ export async function handlePasswordValidation( return passwordValidation } +export type IsCookieValidArgs = { + passwordMethod: PasswordMethod + cookieKey: string + hashedPasswordFromArgs: string | undefined + passwordFromArgs: string | undefined +} + +/** Checks if a req.cookies.key is valid using the PasswordMethod */ +export async function isCookieValid(isCookieValidArgs: IsCookieValidArgs): Promise { + let isValid = false + const { passwordFromArgs = "", cookieKey, hashedPasswordFromArgs = "" } = isCookieValidArgs + switch (isCookieValidArgs.passwordMethod) { + case "PLAIN_TEXT": + isValid = await isHashMatch(passwordFromArgs, cookieKey) + break + case "ARGON2": + case "SHA256": + isValid = safeCompare(cookieKey, hashedPasswordFromArgs) + break + default: + break + } + return isValid +} + const mimeTypes: { [key: string]: string } = { ".aac": "audio/x-aac", ".avi": "video/x-msvideo", diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index 5927ca32..0397fbff 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -6,6 +6,7 @@ import { getPasswordMethod, hashLegacy, isHashLegacyMatch, + isCookieValid, } from "../../../src/node/util" describe("getEnvPaths", () => { @@ -234,7 +235,7 @@ describe("getPasswordMethod", () => { }) }) -describe.only("handlePasswordValidation", () => { +describe("handlePasswordValidation", () => { it("should return true with a hashedPassword for a PLAIN_TEXT password", async () => { const p = "password" const passwordValidation = await handlePasswordValidation({ @@ -322,3 +323,62 @@ describe.only("handlePasswordValidation", () => { expect(matchesHash).toBe(false) }) }) + +describe.only("isCookieValid", () => { + it("should be valid if hashed-password for SHA256 matches cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "SHA256", + cookieKey: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af", + hashedPasswordFromArgs: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af", + passwordFromArgs: undefined, + }) + expect(isValid).toBe(true) + }) + it("should be invalid if hashed-password for SHA256 does not match cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "SHA256", + cookieKey: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb9442bb6f8f8f07af", + hashedPasswordFromArgs: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af", + passwordFromArgs: undefined, + }) + expect(isValid).toBe(false) + }) + it("should be valid if hashed-password for ARGON2 matches cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "ARGON2", + cookieKey: "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + hashedPasswordFromArgs: + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + passwordFromArgs: undefined, + }) + expect(isValid).toBe(true) + }) + it("should be invalid if hashed-password for ARGON2 does not match cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "ARGON2", + cookieKey: "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9H", + hashedPasswordFromArgs: + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + passwordFromArgs: undefined, + }) + expect(isValid).toBe(false) + }) + it("should be valid if password for PLAIN_TEXT matches cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "PLAIN_TEXT", + cookieKey: "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + passwordFromArgs: "password", + hashedPasswordFromArgs: undefined, + }) + expect(isValid).toBe(true) + }) + it("should be invalid if hashed-password for PLAIN_TEXT does not match cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "PLAIN_TEXT", + cookieKey: "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9H", + passwordFromArgs: "password1234", + hashedPasswordFromArgs: undefined, + }) + expect(isValid).toBe(false) + }) +}) From 923761cd78cfb9de374691203bc3b325839d2d40 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 2 Jun 2021 17:24:37 -0700 Subject: [PATCH 21/27] refactor: password logic in http w/ isCookieValid --- src/node/http.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/node/http.ts b/src/node/http.ts index 5160d17f..298e9428 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -8,7 +8,7 @@ import { normalize, Options } from "../common/util" import { AuthType, DefaultedArgs } from "./cli" import { commit, rootPath } from "./constants" import { Heart } from "./heart" -import { isHashMatch } from "./util" +import { getPasswordMethod, handlePasswordValidation, IsCookieValidArgs, isCookieValid, isHashMatch } from "./util" declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -68,14 +68,16 @@ export const authenticated = async (req: express.Request): Promise => { return true case AuthType.Password: // The password is stored in the cookie after being hashed. - // TODO@jsjoeio this also needs to be refactored to check if they're using the legacy password - // or the new one. we can't assume hashed-password means legacy - return !!( - req.cookies.key && - (req.args["hashed-password"] - ? safeCompare(req.cookies.key, req.args["hashed-password"]) - : req.args.password && (await isHashMatch(req.args.password, req.cookies.key))) - ) + const hashedPasswordFromArgs = req.args["hashed-password"] + const passwordMethod = getPasswordMethod(hashedPasswordFromArgs) + const isCookieValidArgs: IsCookieValidArgs = { + passwordMethod, + cookieKey: req.cookies.key as string, + passwordFromArgs: req.args.password || "", + hashedPasswordFromArgs: req.args["hashed-password"], + } + + return await isCookieValid(isCookieValidArgs) default: throw new Error(`Unsupported auth type ${req.args.auth}`) } From 517aaf71c5f3fb377e6f9f047833eb027cb002d1 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Thu, 3 Jun 2021 11:27:59 -0700 Subject: [PATCH 22/27] docs: update FAQ with new hashing instructions --- docs/FAQ.md | 75 ++++++++++++++++++++++--------------------- src/node/http.ts | 3 +- test/unit/cli.test.ts | 2 +- 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 9f86bb3a..0e11308d 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -2,38 +2,39 @@ # FAQ -- [Questions?](#questions) -- [iPad Status?](#ipad-status) -- [Community Projects (awesome-code-server)](#community-projects-awesome-code-server) -- [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration) -- [Differences compared to VS Code?](#differences-compared-to-vs-code) - - [Installing an extension](#installing-an-extension) -- [How can I request a missing extension?](#how-can-i-request-a-missing-extension) -- [Installing an extension manually](#installing-an-extension-manually) -- [How do I configure the marketplace URL?](#how-do-i-configure-the-marketplace-url) -- [Where are extensions stored?](#where-are-extensions-stored) -- [How is this different from VS Code Codespaces?](#how-is-this-different-from-vs-code-codespaces) -- [How should I expose code-server to the internet?](#how-should-i-expose-code-server-to-the-internet) -- [Can I store my password hashed?](#can-i-store-my-password-hashed) -- [How do I securely access web services?](#how-do-i-securely-access-web-services) - - [Sub-paths](#sub-paths) - - [Sub-domains](#sub-domains) -- [Why does the code-server proxy strip `/proxy/` from the request path?](#why-does-the-code-server-proxy-strip-proxyport-from-the-request-path) - - [Proxying to Create React App](#proxying-to-create-react-app) -- [Multi-tenancy](#multi-tenancy) -- [Docker in code-server container?](#docker-in-code-server-container) -- [How can I disable telemetry?](#how-can-i-disable-telemetry) -- [How does code-server decide what workspace or folder to open?](#how-does-code-server-decide-what-workspace-or-folder-to-open) -- [How do I debug issues with code-server?](#how-do-i-debug-issues-with-code-server) -- [Heartbeat File](#heartbeat-file) -- [Healthz endpoint](#healthz-endpoint) -- [How does the config file work?](#how-does-the-config-file-work) -- [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure) -- [How do I make my keyboard shortcuts work?](#how-do-i-make-my-keyboard-shortcuts-work) -- [How do I access my Documents/Downloads/Desktop folders in code-server on OSX?](#how-do-i-access-my-documentsdownloadsdesktop-folders-in-code-server-on-osx) -- [Differences compared to Theia?](#differences-compared-to-theia) -- [`$HTTP_PROXY`, `$HTTPS_PROXY`, `$NO_PROXY`](#http_proxy-https_proxy-no_proxy) -- [Enterprise](#enterprise) +- [FAQ](#faq) + - [Questions?](#questions) + - [iPad Status?](#ipad-status) + - [Community Projects (awesome-code-server)](#community-projects-awesome-code-server) + - [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration) + - [Differences compared to VS Code?](#differences-compared-to-vs-code) + - [Installing an extension](#installing-an-extension) + - [How can I request a missing extension?](#how-can-i-request-a-missing-extension) + - [Installing an extension manually](#installing-an-extension-manually) + - [How do I configure the marketplace URL?](#how-do-i-configure-the-marketplace-url) + - [Where are extensions stored?](#where-are-extensions-stored) + - [How is this different from VS Code Codespaces?](#how-is-this-different-from-vs-code-codespaces) + - [How should I expose code-server to the internet?](#how-should-i-expose-code-server-to-the-internet) + - [Can I store my password hashed?](#can-i-store-my-password-hashed) + - [How do I securely access web services?](#how-do-i-securely-access-web-services) + - [Sub-paths](#sub-paths) + - [Sub-domains](#sub-domains) + - [Why does the code-server proxy strip `/proxy/` from the request path?](#why-does-the-code-server-proxy-strip-proxyport-from-the-request-path) + - [Proxying to Create React App](#proxying-to-create-react-app) + - [Multi-tenancy](#multi-tenancy) + - [Docker in code-server container?](#docker-in-code-server-container) + - [How can I disable telemetry?](#how-can-i-disable-telemetry) + - [How does code-server decide what workspace or folder to open?](#how-does-code-server-decide-what-workspace-or-folder-to-open) + - [How do I debug issues with code-server?](#how-do-i-debug-issues-with-code-server) + - [Heartbeat File](#heartbeat-file) + - [Healthz endpoint](#healthz-endpoint) + - [How does the config file work?](#how-does-the-config-file-work) + - [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure) + - [How do I make my keyboard shortcuts work?](#how-do-i-make-my-keyboard-shortcuts-work) + - [How do I access my Documents/Downloads/Desktop folders in code-server on OSX?](#how-do-i-access-my-documentsdownloadsdesktop-folders-in-code-server-on-osx) + - [Differences compared to Theia?](#differences-compared-to-theia) + - [`$HTTP_PROXY`, `$HTTPS_PROXY`, `$NO_PROXY`](#http_proxy-https_proxy-no_proxy) + - [Enterprise](#enterprise) @@ -205,17 +206,19 @@ Again, please follow [./guide.md](./guide.md) for our recommendations on setting Yes you can! Set the value of `hashed-password` instead of `password`. Generate the hash with: -``` -printf "thisismypassword" | sha256sum | cut -d' ' -f1 +```shell +echo -n "password" | npx argon2-cli -e +$argon2i$v=19$m=4096,t=3,p=1$wst5qhbgk2lu1ih4dmuxvg$ls1alrvdiwtvzhwnzcm1dugg+5dto3dt1d5v9xtlws4 + ``` -Of course replace `thisismypassword` with your actual password. +Of course replace `thisismypassword` with your actual password and **remember to put it inside quotes**! Example: ```yaml auth: password -hashed-password: 1da9133ab9dbd11d2937ec8d312e1e2569857059e73cc72df92e670928983ab5 # You got this from the command above +hashed-password: "$argon2i$v=19$m=4096,t=3,p=1$wST5QhBgk2lu1ih4DMuxvg$LS1alrVdIWtvZHwnzCM1DUGg+5DTO3Dt1d5v9XtLws4" ``` ## How do I securely access web services? diff --git a/src/node/http.ts b/src/node/http.ts index 298e9428..c907dd92 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -2,13 +2,12 @@ import { field, logger } from "@coder/logger" import * as express from "express" import * as expressCore from "express-serve-static-core" import qs from "qs" -import safeCompare from "safe-compare" import { HttpCode, HttpError } from "../common/http" import { normalize, Options } from "../common/util" import { AuthType, DefaultedArgs } from "./cli" import { commit, rootPath } from "./constants" import { Heart } from "./heart" -import { getPasswordMethod, handlePasswordValidation, IsCookieValidArgs, isCookieValid, isHashMatch } from "./util" +import { getPasswordMethod, IsCookieValidArgs, isCookieValid } from "./util" declare global { // eslint-disable-next-line @typescript-eslint/no-namespace diff --git a/test/unit/cli.test.ts b/test/unit/cli.test.ts index 340b1d79..e8164222 100644 --- a/test/unit/cli.test.ts +++ b/test/unit/cli.test.ts @@ -305,7 +305,7 @@ describe("parser", () => { }) }) - it.only("should use env var hashed password", async () => { + it("should use env var hashed password", async () => { process.env.HASHED_PASSWORD = "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY" // test const args = parse([]) From 531b7c0c257a2b97169eed7f602fc9b498db2bef Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Thu, 3 Jun 2021 16:30:33 -0700 Subject: [PATCH 23/27] feat: add splitOnFirstEquals function --- src/node/cli.ts | 23 +++++++++++++++++++++++ src/node/routes/login.ts | 11 +---------- test/unit/cli.test.ts | 39 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 39d38fb3..01c93349 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -240,6 +240,25 @@ export const optionDescriptions = (): string[] => { }) } +export function splitOnFirstEquals(str: string): string[] { + // we use regex instead of "=" to ensure we split at the first + // "=" and return the following substring with it + // important for the hashed-password which looks like this + // $argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY + // 2 means return two items + // Source: https://stackoverflow.com/a/4607799/3015595 + const split = str.split(/=(.+)/, 2) + + // It should always return two elements + // because it's used in a place where + // it expected two elements + if (split.length === 1) { + split.push("") + } + + return split +} + export const parse = ( argv: string[], opts?: { @@ -270,6 +289,7 @@ export const parse = ( let key: keyof Args | undefined let value: string | undefined if (arg.startsWith("--")) { + // TODO fix this const split = arg.replace(/^--/, "").split("=", 2) key = split[0] as keyof Args value = split[1] @@ -543,6 +563,7 @@ export function parseConfigFile(configFile: string, configPath: string): ConfigA const config = yaml.load(configFile, { filename: configPath, }) + console.log("what is this config", config) if (!config || typeof config === "string") { throw new Error(`invalid config: ${config}`) } @@ -555,9 +576,11 @@ export function parseConfigFile(configFile: string, configPath: string): ConfigA } return `--${optName}=${opt}` }) + console.log("what are the configFileArgv", configFileArgv) const args = parse(configFileArgv, { configFile: configPath, }) + console.log(args, "args") return { ...args, config: configPath, diff --git a/src/node/routes/login.ts b/src/node/routes/login.ts index b09585ca..6b8ab4d2 100644 --- a/src/node/routes/login.ts +++ b/src/node/routes/login.ts @@ -2,18 +2,9 @@ import { Router, Request } from "express" import { promises as fs } from "fs" import { RateLimiter as Limiter } from "limiter" import * as path from "path" -import safeCompare from "safe-compare" import { rootPath } from "../constants" import { authenticated, getCookieDomain, redirect, replaceTemplates } from "../http" -import { - getPasswordMethod, - handlePasswordValidation, - hash, - hashLegacy, - humanPath, - isHashLegacyMatch, - isHashMatch, -} from "../util" +import { getPasswordMethod, handlePasswordValidation, humanPath } from "../util" export enum Cookie { Key = "key", diff --git a/test/unit/cli.test.ts b/test/unit/cli.test.ts index e8164222..252bc56c 100644 --- a/test/unit/cli.test.ts +++ b/test/unit/cli.test.ts @@ -3,7 +3,7 @@ import { promises as fs } from "fs" import * as net from "net" import * as os from "os" import * as path from "path" -import { Args, parse, setDefaults, shouldOpenInExistingInstance } from "../../src/node/cli" +import { Args, parse, setDefaults, shouldOpenInExistingInstance, splitOnFirstEquals } from "../../src/node/cli" import { tmpdir } from "../../src/node/constants" import { paths } from "../../src/node/util" @@ -337,6 +337,18 @@ describe("parser", () => { "proxy-domain": ["coder.com", "coder.org"], }) }) + it("should allow '=,$/' in strings", async () => { + const args = parse([ + "--enable-proposed-api", + "$argon2i$v=19$m=4096,t=3,p=1$0qr/o+0t00hsbjfqcksfdq$ofcm4rl6o+b7oxpua4qlxubypbbpsf+8l531u7p9hyy", + ]) + expect(args).toEqual({ + _: [], + "enable-proposed-api": [ + "$argon2i$v=19$m=4096,t=3,p=1$0qr/o+0t00hsbjfqcksfdq$ofcm4rl6o+b7oxpua4qlxubypbbpsf+8l531u7p9hyy", + ], + }) + }) }) describe("cli", () => { @@ -411,3 +423,28 @@ describe("cli", () => { expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined) }) }) + +describe("splitOnFirstEquals", () => { + it("should split on the first equals", () => { + const testStr = "--enabled-proposed-api=test=value" + const actual = splitOnFirstEquals(testStr) + const expected = ["--enabled-proposed-api", "test=value"] + expect(actual).toEqual(expect.arrayContaining(expected)) + }) + it("should split on first equals regardless of multiple equals signs", () => { + const testStr = + "--hashed-password=$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY" + const actual = splitOnFirstEquals(testStr) + const expected = [ + "--hashed-password", + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + ] + expect(actual).toEqual(expect.arrayContaining(expected)) + }) + it("should always return two elements", () => { + const testStr = "" + const actual = splitOnFirstEquals(testStr) + const expected = ["", ""] + expect(actual).toEqual(expect.arrayContaining(expected)) + }) +}) From 8c2bb61af9ef3c7e6824a1f12e6ffbb815118bb6 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Thu, 3 Jun 2021 16:37:46 -0700 Subject: [PATCH 24/27] refactor: parse options with multiple = in cli There was a case with the hashed-password which had multiple equal signs in the value and it wasn't being parsed correctly. This uses a new function and adds a few tests. --- docs/FAQ.md | 66 ++++++++++++++++++++---------------------- package.json | 2 -- src/node/cli.ts | 23 ++------------- src/node/http.ts | 9 ++++-- src/node/util.ts | 6 ++-- test/package.json | 2 -- test/unit/cli.test.ts | 29 ++++++++++++++----- typings/pluginapi.d.ts | 6 +++- 8 files changed, 71 insertions(+), 72 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 0e11308d..b7d57a8c 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -2,39 +2,38 @@ # FAQ -- [FAQ](#faq) - - [Questions?](#questions) - - [iPad Status?](#ipad-status) - - [Community Projects (awesome-code-server)](#community-projects-awesome-code-server) - - [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration) - - [Differences compared to VS Code?](#differences-compared-to-vs-code) - - [Installing an extension](#installing-an-extension) - - [How can I request a missing extension?](#how-can-i-request-a-missing-extension) - - [Installing an extension manually](#installing-an-extension-manually) - - [How do I configure the marketplace URL?](#how-do-i-configure-the-marketplace-url) - - [Where are extensions stored?](#where-are-extensions-stored) - - [How is this different from VS Code Codespaces?](#how-is-this-different-from-vs-code-codespaces) - - [How should I expose code-server to the internet?](#how-should-i-expose-code-server-to-the-internet) - - [Can I store my password hashed?](#can-i-store-my-password-hashed) - - [How do I securely access web services?](#how-do-i-securely-access-web-services) - - [Sub-paths](#sub-paths) - - [Sub-domains](#sub-domains) - - [Why does the code-server proxy strip `/proxy/` from the request path?](#why-does-the-code-server-proxy-strip-proxyport-from-the-request-path) - - [Proxying to Create React App](#proxying-to-create-react-app) - - [Multi-tenancy](#multi-tenancy) - - [Docker in code-server container?](#docker-in-code-server-container) - - [How can I disable telemetry?](#how-can-i-disable-telemetry) - - [How does code-server decide what workspace or folder to open?](#how-does-code-server-decide-what-workspace-or-folder-to-open) - - [How do I debug issues with code-server?](#how-do-i-debug-issues-with-code-server) - - [Heartbeat File](#heartbeat-file) - - [Healthz endpoint](#healthz-endpoint) - - [How does the config file work?](#how-does-the-config-file-work) - - [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure) - - [How do I make my keyboard shortcuts work?](#how-do-i-make-my-keyboard-shortcuts-work) - - [How do I access my Documents/Downloads/Desktop folders in code-server on OSX?](#how-do-i-access-my-documentsdownloadsdesktop-folders-in-code-server-on-osx) - - [Differences compared to Theia?](#differences-compared-to-theia) - - [`$HTTP_PROXY`, `$HTTPS_PROXY`, `$NO_PROXY`](#http_proxy-https_proxy-no_proxy) - - [Enterprise](#enterprise) +- [Questions?](#questions) +- [iPad Status?](#ipad-status) +- [Community Projects (awesome-code-server)](#community-projects-awesome-code-server) +- [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration) +- [Differences compared to VS Code?](#differences-compared-to-vs-code) + - [Installing an extension](#installing-an-extension) +- [How can I request a missing extension?](#how-can-i-request-a-missing-extension) +- [Installing an extension manually](#installing-an-extension-manually) +- [How do I configure the marketplace URL?](#how-do-i-configure-the-marketplace-url) +- [Where are extensions stored?](#where-are-extensions-stored) +- [How is this different from VS Code Codespaces?](#how-is-this-different-from-vs-code-codespaces) +- [How should I expose code-server to the internet?](#how-should-i-expose-code-server-to-the-internet) +- [Can I store my password hashed?](#can-i-store-my-password-hashed) +- [How do I securely access web services?](#how-do-i-securely-access-web-services) + - [Sub-paths](#sub-paths) + - [Sub-domains](#sub-domains) +- [Why does the code-server proxy strip `/proxy/` from the request path?](#why-does-the-code-server-proxy-strip-proxyport-from-the-request-path) + - [Proxying to Create React App](#proxying-to-create-react-app) +- [Multi-tenancy](#multi-tenancy) +- [Docker in code-server container?](#docker-in-code-server-container) +- [How can I disable telemetry?](#how-can-i-disable-telemetry) +- [How does code-server decide what workspace or folder to open?](#how-does-code-server-decide-what-workspace-or-folder-to-open) +- [How do I debug issues with code-server?](#how-do-i-debug-issues-with-code-server) +- [Heartbeat File](#heartbeat-file) +- [Healthz endpoint](#healthz-endpoint) +- [How does the config file work?](#how-does-the-config-file-work) +- [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure) +- [How do I make my keyboard shortcuts work?](#how-do-i-make-my-keyboard-shortcuts-work) +- [How do I access my Documents/Downloads/Desktop folders in code-server on OSX?](#how-do-i-access-my-documentsdownloadsdesktop-folders-in-code-server-on-osx) +- [Differences compared to Theia?](#differences-compared-to-theia) +- [`$HTTP_PROXY`, `$HTTPS_PROXY`, `$NO_PROXY`](#http_proxy-https_proxy-no_proxy) +- [Enterprise](#enterprise) @@ -209,7 +208,6 @@ Yes you can! Set the value of `hashed-password` instead of `password`. Generate ```shell echo -n "password" | npx argon2-cli -e $argon2i$v=19$m=4096,t=3,p=1$wst5qhbgk2lu1ih4dmuxvg$ls1alrvdiwtvzhwnzcm1dugg+5dto3dt1d5v9xtlws4 - ``` Of course replace `thisismypassword` with your actual password and **remember to put it inside quotes**! diff --git a/package.json b/package.json index 32faa1ad..4ca3ef36 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "main": "out/node/entry.js", "devDependencies": { "@schemastore/package": "^0.0.6", - "@types/bcrypt": "^5.0.0", "@types/body-parser": "^1.19.0", "@types/compression": "^1.7.0", "@types/cookie-parser": "^1.4.2", @@ -90,7 +89,6 @@ "dependencies": { "@coder/logger": "1.1.16", "argon2": "^0.28.0", - "bcrypt": "^5.0.1", "body-parser": "^1.19.0", "compression": "^1.7.4", "cookie-parser": "^1.4.5", diff --git a/src/node/cli.ts b/src/node/cli.ts index 01c93349..144e4548 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -247,14 +247,8 @@ export function splitOnFirstEquals(str: string): string[] { // $argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY // 2 means return two items // Source: https://stackoverflow.com/a/4607799/3015595 - const split = str.split(/=(.+)/, 2) - - // It should always return two elements - // because it's used in a place where - // it expected two elements - if (split.length === 1) { - split.push("") - } + // We use the ? to say the the substr after the = is optional + const split = str.split(/=(.+)?/, 2) return split } @@ -289,17 +283,9 @@ export const parse = ( let key: keyof Args | undefined let value: string | undefined if (arg.startsWith("--")) { - // TODO fix this - const split = arg.replace(/^--/, "").split("=", 2) + const split = splitOnFirstEquals(arg.replace(/^--/, "")) key = split[0] as keyof Args value = split[1] - } else { - const short = arg.replace(/^-/, "") - const pair = Object.entries(options).find(([, v]) => v.short === short) - if (pair) { - key = pair[0] as keyof Args - } - } if (!key || !options[key]) { throw error(`Unknown option ${arg}`) @@ -563,7 +549,6 @@ export function parseConfigFile(configFile: string, configPath: string): ConfigA const config = yaml.load(configFile, { filename: configPath, }) - console.log("what is this config", config) if (!config || typeof config === "string") { throw new Error(`invalid config: ${config}`) } @@ -576,11 +561,9 @@ export function parseConfigFile(configFile: string, configPath: string): ConfigA } return `--${optName}=${opt}` }) - console.log("what are the configFileArgv", configFileArgv) const args = parse(configFileArgv, { configFile: configPath, }) - console.log(args, "args") return { ...args, config: configPath, diff --git a/src/node/http.ts b/src/node/http.ts index c907dd92..8b4c9d8c 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -63,9 +63,10 @@ export const ensureAuthenticated = async ( */ export const authenticated = async (req: express.Request): Promise => { switch (req.args.auth) { - case AuthType.None: + case AuthType.None: { return true - case AuthType.Password: + } + case AuthType.Password: { // The password is stored in the cookie after being hashed. const hashedPasswordFromArgs = req.args["hashed-password"] const passwordMethod = getPasswordMethod(hashedPasswordFromArgs) @@ -77,8 +78,10 @@ export const authenticated = async (req: express.Request): Promise => { } return await isCookieValid(isCookieValidArgs) - default: + } + default: { throw new Error(`Unsupported auth type ${req.args.auth}`) + } } } diff --git a/src/node/util.ts b/src/node/util.ts index f575eb52..5a3d0b70 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -1,15 +1,15 @@ +import { logger } from "@coder/logger" +import * as argon2 from "argon2" import * as cp from "child_process" import * as crypto from "crypto" -import * as argon2 from "argon2" import envPaths from "env-paths" import { promises as fs } from "fs" import * as net from "net" import * as os from "os" import * as path from "path" +import safeCompare from "safe-compare" import * as util from "util" import xdgBasedir from "xdg-basedir" -import safeCompare from "safe-compare" -import { logger } from "@coder/logger" export interface Paths { data: string diff --git a/test/package.json b/test/package.json index 773c043f..842cf417 100644 --- a/test/package.json +++ b/test/package.json @@ -17,7 +17,5 @@ }, "resolutions": { "@playwright/test/playwright": "^1.11.0-next-alpha-apr-13-2021" - }, - "dependencies": { } } diff --git a/test/unit/cli.test.ts b/test/unit/cli.test.ts index 252bc56c..2d6e252a 100644 --- a/test/unit/cli.test.ts +++ b/test/unit/cli.test.ts @@ -349,6 +349,21 @@ describe("parser", () => { ], }) }) + it("should parse options with double-dash and multiple equal signs ", async () => { + const args = parse( + [ + "--hashed-password=$argon2i$v=19$m=4096,t=3,p=1$0qr/o+0t00hsbjfqcksfdq$ofcm4rl6o+b7oxpua4qlxubypbbpsf+8l531u7p9hyy", + ], + { + configFile: "/pathtoconfig", + }, + ) + expect(args).toEqual({ + _: [], + "hashed-password": + "$argon2i$v=19$m=4096,t=3,p=1$0qr/o+0t00hsbjfqcksfdq$ofcm4rl6o+b7oxpua4qlxubypbbpsf+8l531u7p9hyy", + }) + }) }) describe("cli", () => { @@ -426,25 +441,25 @@ describe("cli", () => { describe("splitOnFirstEquals", () => { it("should split on the first equals", () => { - const testStr = "--enabled-proposed-api=test=value" + const testStr = "enabled-proposed-api=test=value" const actual = splitOnFirstEquals(testStr) - const expected = ["--enabled-proposed-api", "test=value"] + const expected = ["enabled-proposed-api", "test=value"] expect(actual).toEqual(expect.arrayContaining(expected)) }) it("should split on first equals regardless of multiple equals signs", () => { const testStr = - "--hashed-password=$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY" + "hashed-password=$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY" const actual = splitOnFirstEquals(testStr) const expected = [ - "--hashed-password", + "hashed-password", "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", ] expect(actual).toEqual(expect.arrayContaining(expected)) }) - it("should always return two elements", () => { - const testStr = "" + it("should always return the first element before an equals", () => { + const testStr = "auth=" const actual = splitOnFirstEquals(testStr) - const expected = ["", ""] + const expected = ["auth"] expect(actual).toEqual(expect.arrayContaining(expected)) }) }) diff --git a/typings/pluginapi.d.ts b/typings/pluginapi.d.ts index 881aa1fb..17f3ae19 100644 --- a/typings/pluginapi.d.ts +++ b/typings/pluginapi.d.ts @@ -145,7 +145,11 @@ export const proxy: ProxyServer /** * Middleware to ensure the user is authenticated. Throws if they are not. */ -export function ensureAuthenticated(req: express.Request, res?: express.Response, next?: express.NextFunction): Promise +export function ensureAuthenticated( + req: express.Request, + res?: express.Response, + next?: express.NextFunction, +): Promise /** * Returns true if the user is authenticated. From deaa2242caea82d5a125e9e3b67a4066ccb32b78 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Mon, 7 Jun 2021 14:09:54 -0700 Subject: [PATCH 25/27] feat: add npm_config_build_from_source to build scripts This is necessary due to argon2 being added and an upstream issue where it uses a Linux build that is too new for CentOS 7. --- ci/build/build-standalone-release.sh | 4 ++++ ci/build/npm-postinstall.sh | 3 +++ 2 files changed, 7 insertions(+) diff --git a/ci/build/build-standalone-release.sh b/ci/build/build-standalone-release.sh index e85678a2..a8072a5a 100755 --- a/ci/build/build-standalone-release.sh +++ b/ci/build/build-standalone-release.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash set -euo pipefail +# This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2 +# See: https://github.com/cdr/code-server/pull/3422#pullrequestreview-677765057 +npm_config_build_from_source=true + main() { cd "$(dirname "${0}")/../.." source ./ci/lib.sh diff --git a/ci/build/npm-postinstall.sh b/ci/build/npm-postinstall.sh index 05c93681..ae1be016 100755 --- a/ci/build/npm-postinstall.sh +++ b/ci/build/npm-postinstall.sh @@ -18,6 +18,9 @@ detect_arch() { } ARCH="${NPM_CONFIG_ARCH:-$(detect_arch)}" +# This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2 +# See: https://github.com/cdr/code-server/pull/3422#pullrequestreview-677765057 +npm_config_build_from_source=true main() { # Grabs the major version of node from $npm_config_user_agent which looks like From 3b50bfc17d031661c83a0a029bdb22a7b391e7b0 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Mon, 7 Jun 2021 14:46:59 -0700 Subject: [PATCH 26/27] fix: sanitize password and cookie key --- ci/build/build-standalone-release.sh | 2 +- ci/build/npm-postinstall.sh | 2 +- src/node/http.ts | 4 ++-- src/node/routes/login.ts | 4 ++-- src/node/util.ts | 11 +++++++++++ test/unit/node/util.test.ts | 13 +++++++++++++ 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/ci/build/build-standalone-release.sh b/ci/build/build-standalone-release.sh index a8072a5a..3afde2ea 100755 --- a/ci/build/build-standalone-release.sh +++ b/ci/build/build-standalone-release.sh @@ -3,7 +3,7 @@ set -euo pipefail # This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2 # See: https://github.com/cdr/code-server/pull/3422#pullrequestreview-677765057 -npm_config_build_from_source=true +export npm_config_build_from_source=true main() { cd "$(dirname "${0}")/../.." diff --git a/ci/build/npm-postinstall.sh b/ci/build/npm-postinstall.sh index ae1be016..5e413d7a 100755 --- a/ci/build/npm-postinstall.sh +++ b/ci/build/npm-postinstall.sh @@ -20,7 +20,7 @@ detect_arch() { ARCH="${NPM_CONFIG_ARCH:-$(detect_arch)}" # This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2 # See: https://github.com/cdr/code-server/pull/3422#pullrequestreview-677765057 -npm_config_build_from_source=true +export npm_config_build_from_source=true main() { # Grabs the major version of node from $npm_config_user_agent which looks like diff --git a/src/node/http.ts b/src/node/http.ts index 8b4c9d8c..87a0be10 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -7,7 +7,7 @@ import { normalize, Options } from "../common/util" import { AuthType, DefaultedArgs } from "./cli" import { commit, rootPath } from "./constants" import { Heart } from "./heart" -import { getPasswordMethod, IsCookieValidArgs, isCookieValid } from "./util" +import { getPasswordMethod, IsCookieValidArgs, isCookieValid, sanitizeString } from "./util" declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -72,7 +72,7 @@ export const authenticated = async (req: express.Request): Promise => { const passwordMethod = getPasswordMethod(hashedPasswordFromArgs) const isCookieValidArgs: IsCookieValidArgs = { passwordMethod, - cookieKey: req.cookies.key as string, + cookieKey: sanitizeString(req.cookies.key), passwordFromArgs: req.args.password || "", hashedPasswordFromArgs: req.args["hashed-password"], } diff --git a/src/node/routes/login.ts b/src/node/routes/login.ts index 6b8ab4d2..dfd07ce9 100644 --- a/src/node/routes/login.ts +++ b/src/node/routes/login.ts @@ -4,7 +4,7 @@ import { RateLimiter as Limiter } from "limiter" import * as path from "path" import { rootPath } from "../constants" import { authenticated, getCookieDomain, redirect, replaceTemplates } from "../http" -import { getPasswordMethod, handlePasswordValidation, humanPath } from "../util" +import { getPasswordMethod, handlePasswordValidation, humanPath, sanitizeString } from "../util" export enum Cookie { Key = "key", @@ -61,7 +61,7 @@ router.get("/", async (req, res) => { }) router.post("/", async (req, res) => { - const password = req.body.password + const password = sanitizeString(req.body.password) const hashedPasswordFromArgs = req.args["hashed-password"] try { diff --git a/src/node/util.ts b/src/node/util.ts index 5a3d0b70..6fbabd0f 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -274,6 +274,17 @@ export async function isCookieValid(isCookieValidArgs: IsCookieValidArgs): Promi return isValid } +/** Ensures that the input is sanitized by checking + * - it's a string + * - greater than 0 characters + * - trims whitespace + */ +export function sanitizeString(str: string): string { + // Very basic sanitization of string + // Credit: https://stackoverflow.com/a/46719000/3015595 + return typeof str === "string" && str.trim().length > 0 ? str.trim() : "" +} + const mimeTypes: { [key: string]: string } = { ".aac": "audio/x-aac", ".avi": "video/x-msvideo", diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index 0397fbff..14a67445 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -7,6 +7,7 @@ import { hashLegacy, isHashLegacyMatch, isCookieValid, + sanitizeString, } from "../../../src/node/util" describe("getEnvPaths", () => { @@ -382,3 +383,15 @@ describe.only("isCookieValid", () => { expect(isValid).toBe(false) }) }) + +describe.only("sanitizeString", () => { + it("should return an empty string if passed a type other than a string", () => { + expect(sanitizeString({} as string)).toBe("") + }) + it("should trim whitespace", () => { + expect(sanitizeString(" hello ")).toBe("hello") + }) + it("should always return an empty string", () => { + expect(sanitizeString(" ")).toBe("") + }) +}) From 1e55a648a5572e0985620032971f5cddeea10bbf Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Mon, 7 Jun 2021 15:45:11 -0700 Subject: [PATCH 27/27] feat: check for empty str in isHashMatch --- src/node/cli.ts | 8 ++++++++ src/node/util.ts | 24 ++++++++++++++++-------- test/unit/node/util.test.ts | 16 ++++++++++++++-- yarn.lock | 22 +--------------------- 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 144e4548..a2fac418 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -263,6 +263,7 @@ export const parse = ( if (opts?.configFile) { msg = `error reading ${opts.configFile}: ${msg}` } + return new Error(msg) } @@ -286,6 +287,13 @@ export const parse = ( const split = splitOnFirstEquals(arg.replace(/^--/, "")) key = split[0] as keyof Args value = split[1] + } else { + const short = arg.replace(/^-/, "") + const pair = Object.entries(options).find(([, v]) => v.short === short) + if (pair) { + key = pair[0] as keyof Args + } + } if (!key || !options[key]) { throw error(`Unknown option ${arg}`) diff --git a/src/node/util.ts b/src/node/util.ts index 6fbabd0f..1b7bcd2c 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -134,6 +134,9 @@ export const hash = async (password: string): Promise => { * Used to verify if the password matches the hash */ export const isHashMatch = async (password: string, hash: string) => { + if (password === "" || hash === "") { + return false + } try { return await argon2.verify(hash, password) } catch (error) { @@ -209,11 +212,12 @@ type HandlePasswordValidationArgs = { * Checks if a password is valid and also returns the hash * using the PasswordMethod */ -export async function handlePasswordValidation( - passwordValidationArgs: HandlePasswordValidationArgs, -): Promise { - const { passwordMethod, passwordFromArgs, passwordFromRequestBody, hashedPasswordFromArgs } = passwordValidationArgs - // TODO implement +export async function handlePasswordValidation({ + passwordMethod, + passwordFromArgs, + passwordFromRequestBody, + hashedPasswordFromArgs, +}: HandlePasswordValidationArgs): Promise { const passwordValidation = { isPasswordValid: false, hashedPassword: "", @@ -257,10 +261,14 @@ export type IsCookieValidArgs = { } /** Checks if a req.cookies.key is valid using the PasswordMethod */ -export async function isCookieValid(isCookieValidArgs: IsCookieValidArgs): Promise { +export async function isCookieValid({ + passwordFromArgs = "", + cookieKey, + hashedPasswordFromArgs = "", + passwordMethod, +}: IsCookieValidArgs): Promise { let isValid = false - const { passwordFromArgs = "", cookieKey, hashedPasswordFromArgs = "" } = isCookieValidArgs - switch (isCookieValidArgs.passwordMethod) { + switch (passwordMethod) { case "PLAIN_TEXT": isValid = await isHashMatch(passwordFromArgs, cookieKey) break diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index 14a67445..a0679177 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -185,6 +185,18 @@ describe("isHashMatch", () => { const actual = await isHashMatch(password, _hash) expect(actual).toBe(true) }) + it("should return false if the password is empty", async () => { + const password = "" + const _hash = "$argon2i$v=19$m=4096,t=3,p=1$EAoczTxVki21JDfIZpTUxg$rkXgyrW4RDGoDYrxBFD4H2DlSMEhP4h+Api1hXnGnFY" + const actual = await isHashMatch(password, _hash) + expect(actual).toBe(false) + }) + it("should return false if the hash is empty", async () => { + const password = "hellowpasssword" + const _hash = "" + const actual = await isHashMatch(password, _hash) + expect(actual).toBe(false) + }) }) describe("hashLegacy", () => { @@ -325,7 +337,7 @@ describe("handlePasswordValidation", () => { }) }) -describe.only("isCookieValid", () => { +describe("isCookieValid", () => { it("should be valid if hashed-password for SHA256 matches cookie.key", async () => { const isValid = await isCookieValid({ passwordMethod: "SHA256", @@ -384,7 +396,7 @@ describe.only("isCookieValid", () => { }) }) -describe.only("sanitizeString", () => { +describe("sanitizeString", () => { it("should return an empty string if passed a type other than a string", () => { expect(sanitizeString({} as string)).toBe("") }) diff --git a/yarn.lock b/yarn.lock index c2071104..677b8fab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -896,7 +896,7 @@ resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== -"@mapbox/node-pre-gyp@^1.0.0", "@mapbox/node-pre-gyp@^1.0.1": +"@mapbox/node-pre-gyp@^1.0.1": version "1.0.5" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA== @@ -1059,13 +1059,6 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== -"@types/bcrypt@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-5.0.0.tgz#a835afa2882d165aff5690893db314eaa98b9f20" - integrity sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw== - dependencies: - "@types/node" "*" - "@types/body-parser@*", "@types/body-parser@^1.19.0": version "1.19.0" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" @@ -1773,14 +1766,6 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -bcrypt@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.0.1.tgz#f1a2c20f208e2ccdceea4433df0c8b2c54ecdf71" - integrity sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.0" - node-addon-api "^3.1.0" - binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -5432,11 +5417,6 @@ node-addon-api@^3.0.2: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-addon-api@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.0.tgz#7028b56a7eb572b73873aed731a7f9c9365f5ee4" - integrity sha512-kcwSAWhPi4+QzAtsL2+2s/awvDo2GKLsvMCwNRxb5BUshteXU8U97NCyvQDsGKs/m0He9WcG4YWew/BnuLx++w== - node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"