gina 0.5.26 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +34 -22
- package/ROADMAP.md +43 -16
- package/bin/cli +12 -1
- package/bin/gina-init +15 -1
- package/framework/v0.6.0/VERSION +1 -0
- package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/js/gina.js +36 -1
- package/framework/v0.6.0/core/asset/plugin/dist/vendor/gina/js/gina.min.js +601 -0
- package/framework/v0.6.0/core/asset/plugin/dist/vendor/gina/js/gina.min.js.br +0 -0
- package/framework/v0.6.0/core/asset/plugin/dist/vendor/gina/js/gina.min.js.gz +0 -0
- package/framework/{v0.5.26 → v0.6.0}/core/connectors/couchbase/lib/session-store.v3.js +52 -84
- package/framework/{v0.5.26 → v0.6.0}/core/connectors/couchbase/lib/session-store.v4.js +58 -84
- package/framework/{v0.5.26 → v0.6.0}/core/connectors/mongodb/lib/session-store.js +5 -2
- package/framework/{v0.5.26 → v0.6.0}/core/connectors/redis/lib/session-store.js +5 -2
- package/framework/{v0.5.26 → v0.6.0}/core/connectors/scylladb/lib/session-store.js +5 -2
- package/framework/{v0.5.26 → v0.6.0}/core/connectors/sqlite/lib/session-store.js +17 -3
- package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/session/src/main.js +142 -9
- package/framework/{v0.5.26 → v0.6.0}/core/router.js +101 -11
- package/framework/{v0.5.26 → v0.6.0}/core/server.js +91 -1
- package/framework/{v0.5.26 → v0.6.0}/core/template/conf/settings.json +3 -0
- package/framework/v0.6.0/lib/audit/src/main.js +916 -0
- package/framework/v0.6.0/lib/authn/package.json +7 -0
- package/framework/v0.6.0/lib/authn/src/lockout.js +615 -0
- package/framework/v0.6.0/lib/authn/src/main.js +899 -0
- package/framework/v0.6.0/lib/authn/src/totp.js +418 -0
- package/framework/v0.6.0/lib/cmd/audit/arguments.json +5 -0
- package/framework/v0.6.0/lib/cmd/audit/help.txt +50 -0
- package/framework/v0.6.0/lib/cmd/audit/verify.js +294 -0
- package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/init.js +11 -0
- package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/tail.js +5 -1
- package/framework/{v0.5.26 → v0.6.0}/lib/cmd/gina.1.md +1 -0
- package/framework/{v0.5.26 → v0.6.0}/lib/index.js +13 -0
- package/framework/{v0.5.26 → v0.6.0}/lib/logger/src/containers/file/index.js +11 -1
- package/framework/{v0.5.26 → v0.6.0}/lib/logger/src/containers/mq/speaker.js +8 -1
- package/framework/v0.6.0/lib/net-locality/package.json +17 -0
- package/framework/v0.6.0/lib/net-locality/src/main.js +174 -0
- package/framework/{v0.5.26 → v0.6.0}/lib/nunjucks-filters/src/main.js +13 -0
- package/framework/{v0.5.26 → v0.6.0}/lib/routing/src/main.js +36 -1
- package/framework/{v0.5.26 → v0.6.0}/lib/swig-filters/src/main.js +13 -0
- package/framework/{v0.5.26 → v0.6.0}/package.json +1 -1
- package/gna.js +4 -4
- package/llms.txt +19 -2
- package/package.json +3 -3
- package/schema/connectors.json +2 -3
- package/schema/settings.json +101 -0
- package/types/index.d.ts +4 -0
- package/framework/v0.5.26/VERSION +0 -1
- package/framework/v0.5.26/core/asset/plugin/dist/vendor/gina/js/gina.min.js +0 -599
- package/framework/v0.5.26/core/asset/plugin/dist/vendor/gina/js/gina.min.js.br +0 -0
- package/framework/v0.5.26/core/asset/plugin/dist/vendor/gina/js/gina.min.js.gz +0 -0
- package/framework/v0.5.26/lib/audit/src/main.js +0 -456
- /package/framework/{v0.5.26 → v0.6.0}/AUTHORS +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/LICENSE +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/html/nolayout.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/html/static.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/img/android-chrome-192x192.png +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/img/android-chrome-512x512.png +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/img/apple-touch-icon.png +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/img/favicon-16x16.png +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/img/favicon-32x32.png +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/img/favicon.ico +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.css +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/beemaster/index.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/css/gina.min.css +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/css/gina.min.css.br +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/css/gina.min.css.gz +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/html/statusbar.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/html/statusbar.html.br +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/html/statusbar.html.gz +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/inspector/have_heart_one-webfont.woff2 +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/inspector/index.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/inspector/inspector.css +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/inspector/inspector.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/inspector/logo.svg +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.br +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.gz +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/config.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/ai/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/ai/lib/connector.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/couchbase/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/couchbase/lib/connector.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/couchbase/lib/connector.v3.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/couchbase/lib/connector.v4.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/couchbase/lib/n1ql.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/couchbase/lib/session-store.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/mongodb/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/mongodb/lib/connector.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/mongodb/lib/job-store.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/mongodb/lib/pipeline-loader.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/mysql/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/mysql/lib/connector.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/postgresql/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/postgresql/lib/connector.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/redis/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/redis/lib/connector.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/redis/lib/job-store.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/redis/lib/render-cache-store.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/scylladb/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/scylladb/lib/connector.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/sql-parser.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/sqlite/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/sqlite/lib/connector.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/connectors/sqlite/lib/job-store.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/content.encoding +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/controller.framework.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/controller.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/controller.render-json.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/controller.render-nunjucks-async.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/controller.render-nunjucks.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/controller.render-stream.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/controller.render-swig-async.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/controller.render-swig.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/controller.render-v1.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/inspector-window-emit.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/controller/release-banner.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/dev/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/dev/lib/class.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/dev/lib/factory.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/dev/lib/tools.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/gna.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/locales/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/locales/currency.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/locales/dist/language/en.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/locales/dist/language/fr.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/locales/dist/region/en.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/locales/dist/region/fr.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/locales/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/mime.types +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/model/entity.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/model/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/model/template/entityFactory.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/model/template/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/csrf/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/csrf/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/csrf/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/coep/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/coep/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/coep/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/coop/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/coop/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/coop/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/corp/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/corp/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/corp/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/csp/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/csp/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/csp/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/hide-powered-by/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/hide-powered-by/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/hide-powered-by/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/hsts/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/hsts/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/hsts/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/origin-agent-cluster/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/origin-agent-cluster/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/origin-agent-cluster/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/referrer-policy/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/referrer-policy/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/referrer-policy/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-content-type-options/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-content-type-options/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-content-type-options/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-dns-prefetch-control/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-dns-prefetch-control/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-dns-prefetch-control/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-download-options/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-download-options/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-download-options/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-frame-options/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-frame-options/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-frame-options/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-xss-protection/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-xss-protection/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/security-headers/x-xss-protection/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/session/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/session/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/storage/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/storage/build.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/storage/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/storage/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/validator/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/validator/build.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/validator/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/validator/src/form-validator.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/plugins/lib/validator/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/server.express.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/server.isaac.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/status.codes +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/_gitignore +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/config/app.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/config/connectors.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/config/routing.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/config/settings.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/config/settings.server.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/config/templates.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/config/watchers.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/controllers/controller.content.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/controllers/controller.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/controllers/setup.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle/locales/en.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_namespace/controllers/controller.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_public/css/default.css +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_public/css/home.css +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_public/css/vendor/readme.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_public/favicon.ico +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_public/js/components/x-checklist.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_public/js/vendor/readme.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_public/manifest.webmanifest +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_public/readme.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_public/sw.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_templates/handlers/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_templates/html/content/homepage.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_templates/html/includes/error-msg-noscript.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_templates/html/includes/error-msg-outdated-browser.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_templates/html/includes/x-checklist.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/boilerplate/bundle_templates/html/layouts/main.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/command/gina.bat.tpl +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/command/gina.tpl +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/conf/env.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/conf/manifest.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/conf/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/conf/statics.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/conf/templates.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/error/client/json/401.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/error/client/json/403.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/error/client/json/404.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/error/server/html/50x.html +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/error/server/json/500.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/error/server/json/503.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/core/template/extensions/logger/config.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/console.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/context.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/data/LICENSE +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/data/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/data/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/data/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/dateFormat.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/json/LICENSE +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/json/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/json/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/json/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/path.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/plugins/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/plugins/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/plugins/src/api-error.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/plugins/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/prototypes.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/task.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/helpers/text.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/admin/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/admin/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/archiver/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/archiver/build.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/archiver/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/archiver/src/dep/jszip.min.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/archiver/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/async/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/async/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/audit/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/audit-store.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/authz-gate/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/authz-gate/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cache/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cache/build.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cache/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cache/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/aliases.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/add.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/build.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/copy.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/cp.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/inc/name-rewrite.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/man.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/mcp-start.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/mcp.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/oas.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/openapi.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/remove.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/rename.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/restart.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/rm.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/start.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/status.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/stop.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/bundle/types.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/cache/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/cache/clear.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/cache/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/cache/stats.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/add.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/infer.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/migrate.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/models.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/remove.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/rm.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/connector/test.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/container/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/container/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/container/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/container/man.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/container/ps.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/container/stop.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/add.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/inc/args.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/inc/namespace.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/inc/reference-rewrite.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/inc/reference-scan.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/inc/scaffold.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/remove.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/rename.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/controller/rm.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/add.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/get.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/link-dev.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/remove.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/rm.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/set.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/unset.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/env/use.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/add.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/build.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/dot.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/get.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/link-node-modules.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/link.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/man.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/msg.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/open.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/remove.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/reset.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/restart.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/set.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/start.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/status.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/stop.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/update.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/framework/version.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/gina-dev.1.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/gina-framework.1.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/helper.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/i18n/add.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/i18n/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/i18n/export.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/i18n/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/i18n/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/i18n/import.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/i18n/scan.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/image/_host.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/image/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/image/build.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/image/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/image/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/image/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/image/man.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/image/rm.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/image/run.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/inspector/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/inspector/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/inspector/open.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/man-render.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/minion/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/minion/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/minion/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/minion/kill.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/minion/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/msg.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/port/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/port/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/port/inc/scan.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/port/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/port/reset.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/port/set.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/add.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/backup.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/build.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/import.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/man.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/move.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/remove.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/rename.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/restart.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/restore.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/rm.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/start.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/status.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/project/stop.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/protocol/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/protocol/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/protocol/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/protocol/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/protocol/remove.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/protocol/set.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/scope/add.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/scope/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/scope/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/scope/link-local.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/scope/link-production.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/scope/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/scope/remove.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/scope/rm.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/scope/use.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/secrets/arguments.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/secrets/check.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/secrets/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/secrets/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/secrets/scan.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/service/help.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/service/help.txt +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/service/list.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/service/man.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/service/start.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd/view/add.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd-status-format/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cmd-status-format/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/collection/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/collection/build.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/collection/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/collection/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/config.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/connector-config/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/connector-config/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/connector-error/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/connector-error/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/connector-registry/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/connector-registry/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cron/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cron/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/cron/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/domain/LICENSE +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/domain/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/domain/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/domain/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/dto/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/dto/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/dto-pipe/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/dto-pipe/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/dto-types/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/dto-types/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/generator/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/i18n/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/i18n/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/image-build/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/image-build/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/inherits/LICENSE +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/inherits/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/inherits/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/inherits/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/inspector-events/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/inspector-events/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/inspector-redact/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/inspector-redact/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/instrument/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/instrument/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/job/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/job/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/job-store.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/json-config-header/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/json-config-header/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/logger/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/logger/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/logger/src/containers/default/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/logger/src/containers/file/lib/logrotator/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/logger/src/containers/file/lib/logrotator/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/logger/src/containers/mq/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/logger/src/containers/mq/listener.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/logger/src/helper.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/logger/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/math/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/mcp-dispatch/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/mcp-dispatch/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/mcp-http/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/mcp-http/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/mcp-server/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/mcp-server/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/merge/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/merge/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/merge/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/metrics/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/metrics/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/model.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/nunjucks-filters/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/nunjucks-filters/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/nunjucks-resolver/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/nunjucks-resolver/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/proc.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/release-watch/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/release-watch/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/render-cache/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/render-cache/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/render-cache-store.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/routing/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/routing/build.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/routing/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/routing/src/radix.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/routing-introspect/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/routing-introspect/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/secrets/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/secrets/src/backends/env.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/secrets/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/session-store.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/shell.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/state.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/swig-filters/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/swig-filters/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/swig-resolver/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/swig-resolver/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/template-loaders/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/template-loaders/src/loaders/http.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/template-loaders/src/loaders/memory.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/template-loaders/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/url/README.md +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/url/index.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/url/routing.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/uuid/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/uuid/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/validator.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/watcher/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/watcher/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/ws-framing/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/ws-framing/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/ws-query/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/ws-query/src/main.js +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/ws-session/package.json +0 -0
- /package/framework/{v0.5.26 → v0.6.0}/lib/ws-session/src/main.js +0 -0
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the gina package.
|
|
3
|
+
* Copyright (c) 2009-2026 Rhinostone <contact@gina.io>
|
|
4
|
+
*
|
|
5
|
+
* For the full copyright and license information, please view the LICENSE
|
|
6
|
+
* file that was distributed with this source code.
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* lib/authn — authentication hardening primitives (#COMPLY3).
|
|
12
|
+
*
|
|
13
|
+
* **Hooks and helpers, never an identity provider.** Gina does not own a user
|
|
14
|
+
* record, a credential store, a login route, or a session id — those are the
|
|
15
|
+
* bundle's. What this module supplies is the handful of primitives that are
|
|
16
|
+
* dangerous to hand-roll: a memory-hard password hash with a self-describing
|
|
17
|
+
* encoding, a constant-time verify, and a password policy check.
|
|
18
|
+
*
|
|
19
|
+
* It composes with what already ships rather than replacing it:
|
|
20
|
+
*
|
|
21
|
+
* 1. `validatePasswordPolicy(pw)` — at registration / password change.
|
|
22
|
+
* 2. `hashPassword(pw)` — store the returned PHC string.
|
|
23
|
+
* 3. `createLockout()` — refuse an account under attack BEFORE
|
|
24
|
+
* spending a KDF on it.
|
|
25
|
+
* 4. `verifyPassword(pw, stored)` — at login; pair it with `dummyVerify` on
|
|
26
|
+
* the account-not-found branch.
|
|
27
|
+
* 5. `req.login(user, done)` — core/router.js rotates the session id and
|
|
28
|
+
* binds `req.session.user` (#COMPLY4).
|
|
29
|
+
*
|
|
30
|
+
* Step 5 is where authentication ENDS and authorization begins: `lib/authz-gate`
|
|
31
|
+
* reads `req.session.user`, so nothing here ever touches a session.
|
|
32
|
+
*
|
|
33
|
+
* **Encoding.** Hashes are PHC strings — `$scrypt$ln=17,r=8,p=1$<salt>$<hash>`
|
|
34
|
+
* (base64, unpadded). Self-describing, so the parameters travel with the hash
|
|
35
|
+
* and can be raised later without a flag day: {@link needsRehash} reports which
|
|
36
|
+
* stored hashes are below the current policy, and the login action upgrades
|
|
37
|
+
* them transparently (it is the only moment the plaintext is in hand).
|
|
38
|
+
*
|
|
39
|
+
* **Why scrypt.** It is memory-hard and lives in node core, so a consumer needs
|
|
40
|
+
* no native addon to get a defensible default — the framework must work out of
|
|
41
|
+
* the box. Argon2id is the stronger primitive where a consumer can install it;
|
|
42
|
+
* {@link verifyPassword} therefore ALSO verifies `$argon2*$` and `$2a/2b/2y$`
|
|
43
|
+
* (bcrypt) hashes through the consumer's own `argon2` / `bcrypt` package, so a
|
|
44
|
+
* bundle migrating onto gina keeps its existing hashes working. Gina never
|
|
45
|
+
* MINTS those formats — one mint path, one set of parameters to reason about.
|
|
46
|
+
*
|
|
47
|
+
* **What this module deliberately does NOT do:** no credential storage, no user
|
|
48
|
+
* lookup, no login route, no session handling, no password reset or email
|
|
49
|
+
* flows, no breach-corpus checks (that needs a network service — see the guide
|
|
50
|
+
* for the k-anonymity pattern), no password history (it needs the store gina
|
|
51
|
+
* does not own).
|
|
52
|
+
*
|
|
53
|
+
* @module lib/authn
|
|
54
|
+
* @see module:lib/authz-gate — the authorization half; reads `req.session.user`
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* // in a bundle's login action
|
|
58
|
+
* var authn = require('gina').lib.authn;
|
|
59
|
+
*
|
|
60
|
+
* authn.verifyPassword(self.post.password, account.passwordHash, function (err, ok) {
|
|
61
|
+
* if (err) { return self.throwError(500); }
|
|
62
|
+
* if (!ok) { return self.renderJSON({ error: 'invalid credentials' }); }
|
|
63
|
+
* self.req.login({ id: account.id, roles: account.roles }, function (loginErr) {
|
|
64
|
+
* if (loginErr) { return self.throwError(500); }
|
|
65
|
+
* self.redirect('/dashboard');
|
|
66
|
+
* });
|
|
67
|
+
* });
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
var crypto = require('crypto');
|
|
71
|
+
var lockout = require('./lockout');
|
|
72
|
+
var totp = require('./totp');
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Current scrypt cost parameters.
|
|
76
|
+
*
|
|
77
|
+
* `ln` is the base-2 logarithm of scrypt's `N`: `N = 2^17 = 131072`, which with
|
|
78
|
+
* `r = 8` puts the working set at roughly 128 MiB and a single hash in the
|
|
79
|
+
* ~100 ms range on 2026 server hardware — the OWASP Password Storage guidance
|
|
80
|
+
* for scrypt. `p = 1` keeps one hash on one core so the cost is memory, not
|
|
81
|
+
* parallelism.
|
|
82
|
+
*
|
|
83
|
+
* Raising these is a supported change: existing hashes keep verifying (their
|
|
84
|
+
* own parameters are encoded in them) and {@link needsRehash} starts reporting
|
|
85
|
+
* `true` for them.
|
|
86
|
+
*
|
|
87
|
+
* @constant
|
|
88
|
+
* @type {{ln: number, r: number, p: number}}
|
|
89
|
+
* @inner
|
|
90
|
+
* @private
|
|
91
|
+
*/
|
|
92
|
+
var SCRYPT_PARAMS = { ln: 17, r: 8, p: 1 };
|
|
93
|
+
|
|
94
|
+
/** @constant {number} Salt length in bytes (128 bits). @inner @private */
|
|
95
|
+
var SALT_BYTES = 16;
|
|
96
|
+
/** @constant {number} Derived-key length in bytes (256 bits). @inner @private */
|
|
97
|
+
var KEY_BYTES = 32;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Upper bound on an accepted password, in bytes of UTF-8.
|
|
101
|
+
*
|
|
102
|
+
* A memory-hard KDF is a denial-of-service surface: the work is paid by the
|
|
103
|
+
* server, and an unbounded input lets a caller choose how much. 1024 bytes is
|
|
104
|
+
* far above any real passphrase and far below anything that matters. Rejected
|
|
105
|
+
* as an ERROR rather than silently truncated — a silent truncation would make
|
|
106
|
+
* two different passwords authenticate each other.
|
|
107
|
+
*
|
|
108
|
+
* @constant
|
|
109
|
+
* @type {number}
|
|
110
|
+
* @inner
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
var MAX_PASSWORD_BYTES = 1024;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Default minimum password LENGTH.
|
|
117
|
+
*
|
|
118
|
+
* 12 with no composition rules, per NIST SP 800-63B: mandatory character-class
|
|
119
|
+
* mixing measurably pushes users toward predictable substitutions, so length is
|
|
120
|
+
* the requirement and composition is available but off.
|
|
121
|
+
*
|
|
122
|
+
* @constant
|
|
123
|
+
* @type {number}
|
|
124
|
+
* @inner
|
|
125
|
+
* @private
|
|
126
|
+
*/
|
|
127
|
+
var DEFAULT_MIN_LENGTH = 12;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Concurrency ceiling for in-flight scrypt operations.
|
|
131
|
+
*
|
|
132
|
+
* Each hash reserves ~128 MiB and occupies a libuv threadpool slot (default
|
|
133
|
+
* size 4). Without a ceiling, a burst of login attempts is a memory-exhaustion
|
|
134
|
+
* vector and starves every other threadpool user — fs, zlib, dns. Two keeps a
|
|
135
|
+
* burst bounded while leaving threadpool capacity for the rest of the process.
|
|
136
|
+
*
|
|
137
|
+
* @constant
|
|
138
|
+
* @type {number}
|
|
139
|
+
* @inner
|
|
140
|
+
* @private
|
|
141
|
+
*/
|
|
142
|
+
var DEFAULT_MAX_CONCURRENT = 2;
|
|
143
|
+
|
|
144
|
+
/** @type {number} In-flight scrypt operations. @inner @private */
|
|
145
|
+
var _inFlight = 0;
|
|
146
|
+
/** @type {Array<function>} FIFO of queued scrypt starters. @inner @private */
|
|
147
|
+
var _queue = [];
|
|
148
|
+
/** @type {number} Effective concurrency ceiling. @inner @private */
|
|
149
|
+
var _maxConcurrent = DEFAULT_MAX_CONCURRENT;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Ceiling on the number of hashes WAITING for a slot.
|
|
153
|
+
*
|
|
154
|
+
* The concurrency gauge bounds how much work runs at once; without this, it did
|
|
155
|
+
* not bound how much work could be PROMISED. Set generously — real login
|
|
156
|
+
* traffic never approaches it — so reaching it means the endpoint is under load
|
|
157
|
+
* that belongs to a rate limiter, not to a KDF.
|
|
158
|
+
*
|
|
159
|
+
* @constant
|
|
160
|
+
* @type {number}
|
|
161
|
+
* @inner
|
|
162
|
+
* @private
|
|
163
|
+
*/
|
|
164
|
+
var DEFAULT_MAX_QUEUE = 100;
|
|
165
|
+
/** @type {number} Effective queue ceiling. @inner @private */
|
|
166
|
+
var _maxQueue = DEFAULT_MAX_QUEUE;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Run `fn` once a concurrency slot is free, releasing the slot when it calls
|
|
170
|
+
* back.
|
|
171
|
+
*
|
|
172
|
+
* FIFO, so a queued attempt cannot be starved by later arrivals. `release` is
|
|
173
|
+
* latched: a KDF callback that fired twice would otherwise corrupt the counter
|
|
174
|
+
* and permanently shrink the pool.
|
|
175
|
+
*
|
|
176
|
+
* @param {function} fn - receives `release` and must call it exactly once.
|
|
177
|
+
* @returns {void}
|
|
178
|
+
* @inner
|
|
179
|
+
* @private
|
|
180
|
+
*/
|
|
181
|
+
function withSlot(fn, onRejected) {
|
|
182
|
+
function start() {
|
|
183
|
+
_inFlight++;
|
|
184
|
+
var released = false;
|
|
185
|
+
fn(function release() {
|
|
186
|
+
if (released) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
released = true;
|
|
190
|
+
_inFlight--;
|
|
191
|
+
if (_queue.length > 0) {
|
|
192
|
+
var next = _queue.shift();
|
|
193
|
+
next();
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (_inFlight < _maxConcurrent) {
|
|
198
|
+
return start();
|
|
199
|
+
}
|
|
200
|
+
// Shed load rather than queue without limit. An unbounded queue made the
|
|
201
|
+
// documented account-not-found pattern an amplifier: 30 requests for
|
|
202
|
+
// accounts that DO NOT EXIST — no credentials, no account needed — pushed a
|
|
203
|
+
// legitimate login from 25 ms to 431 ms (17x), growing linearly. Bounded
|
|
204
|
+
// rejection is a far better failure than unbounded latency, and it applies
|
|
205
|
+
// to every caller equally, so it opens no enumeration difference.
|
|
206
|
+
if (_queue.length >= _maxQueue) {
|
|
207
|
+
var err = new Error('[gina authn] password-hashing queue is full (' + _maxQueue + ' waiting) — shedding load. Respond 503 and retry; if this is steady-state, the login endpoint needs request-rate limiting in front of it.');
|
|
208
|
+
err.code = 'AUTHN_QUEUE_FULL';
|
|
209
|
+
if (typeof onRejected === 'function') {
|
|
210
|
+
return process.nextTick(function () { onRejected(err); });
|
|
211
|
+
}
|
|
212
|
+
throw err;
|
|
213
|
+
}
|
|
214
|
+
_queue.push(start);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Set the scrypt concurrency ceiling.
|
|
219
|
+
*
|
|
220
|
+
* Raise it only alongside a matching threadpool size (`UV_THREADPOOL_SIZE`) and
|
|
221
|
+
* a memory budget. The peak is `maxConcurrent` times the working set of the
|
|
222
|
+
* parameters actually in use — 128 MiB each at the shipped defaults, but up to
|
|
223
|
+
* the `MAX_WORKING_SET_MIB` ceiling for a stored hash carrying a higher `r`,
|
|
224
|
+
* since a stored hash chooses its own cost.
|
|
225
|
+
*
|
|
226
|
+
* @param {number} n - positive integer.
|
|
227
|
+
* @returns {void}
|
|
228
|
+
* @throws {Error} when `n` is not a positive integer.
|
|
229
|
+
* @memberof module:lib/authn
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* lib.authn.setMaxConcurrentHashes(4); // needs UV_THREADPOOL_SIZE >= 6 and ~512 MiB headroom
|
|
233
|
+
*/
|
|
234
|
+
function setMaxConcurrentHashes(n) {
|
|
235
|
+
if (typeof n !== 'number' || !isFinite(n) || n < 1 || Math.floor(n) !== n) {
|
|
236
|
+
throw new Error('[gina authn] setMaxConcurrentHashes(n) expects a positive integer — got: ' + JSON.stringify(n));
|
|
237
|
+
}
|
|
238
|
+
_maxConcurrent = n;
|
|
239
|
+
while (_inFlight < _maxConcurrent && _queue.length > 0) {
|
|
240
|
+
var next = _queue.shift();
|
|
241
|
+
next();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Minimum accepted lengths, in bytes, for the SALT and KEY fields of a stored
|
|
247
|
+
* hash.
|
|
248
|
+
*
|
|
249
|
+
* A stored hash is attacker-controlled once the credential store is — and it is
|
|
250
|
+
* also whatever a bad migration or a truncated column left behind. Since
|
|
251
|
+
* {@link verifyPassword} derives exactly as many bytes as the stored key holds,
|
|
252
|
+
* a SHORT key silently weakens the comparison to that many bytes: measured, a
|
|
253
|
+
* 1-byte stored key authenticated 1 of 60 random passwords. Anything below
|
|
254
|
+
* these floors was not minted here, so it is rejected rather than compared.
|
|
255
|
+
*
|
|
256
|
+
* @constant
|
|
257
|
+
* @type {number}
|
|
258
|
+
* @inner
|
|
259
|
+
* @private
|
|
260
|
+
*/
|
|
261
|
+
var MIN_STORED_KEY_BYTES = 16;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Accepted ranges for the cost parameters, and the ceiling on the working set
|
|
265
|
+
* they imply.
|
|
266
|
+
*
|
|
267
|
+
* Bounding each parameter INDEPENDENTLY is not enough: scrypt's working set is
|
|
268
|
+
* `128 * 2^ln * r`, so the corner of the allowed box — `ln=20, r=32` — implies
|
|
269
|
+
* **4 GiB per verify**, 32x the documented peak, from a stored string an
|
|
270
|
+
* attacker controls once the credential store does. `p` multiplies CPU rather
|
|
271
|
+
* than memory (measured: `p=16` cost 14x the wall time of `p=1` at the same
|
|
272
|
+
* memory), so it is bounded separately and tightly — we mint `p=1`.
|
|
273
|
+
*
|
|
274
|
+
* `MAX_WORKING_SET_MIB` is set so the whole documented mint range stays legal at
|
|
275
|
+
* the standard `r=8` (`ln=20, r=8` is exactly 1024 MiB) while the pathological
|
|
276
|
+
* corners are refused.
|
|
277
|
+
*
|
|
278
|
+
* @constant
|
|
279
|
+
* @inner
|
|
280
|
+
* @private
|
|
281
|
+
*/
|
|
282
|
+
var PARAM_BOUNDS = { lnMin: 14, lnMax: 20, rMin: 1, rMax: 32, pMin: 1, pMax: 4 };
|
|
283
|
+
/** @constant {number} Ceiling on `128 * 2^ln * r`, in MiB. @inner @private */
|
|
284
|
+
var MAX_WORKING_SET_MIB = 1024;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Validate a cost triple against the shared bounds AND the working-set ceiling.
|
|
288
|
+
*
|
|
289
|
+
* Applied identically when minting and when reading a stored hash — an
|
|
290
|
+
* asymmetry between the two is how {@link hashPassword} was able to mint hashes
|
|
291
|
+
* {@link verifyPassword} could never read.
|
|
292
|
+
*
|
|
293
|
+
* @param {number} ln
|
|
294
|
+
* @param {number} r
|
|
295
|
+
* @param {number} p
|
|
296
|
+
* @returns {boolean}
|
|
297
|
+
* @inner
|
|
298
|
+
* @private
|
|
299
|
+
*/
|
|
300
|
+
function costWithinBounds(ln, r, p) {
|
|
301
|
+
if (!isFinite(ln) || !isFinite(r) || !isFinite(p)) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
if (Math.floor(ln) !== ln || Math.floor(r) !== r || Math.floor(p) !== p) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
if (ln < PARAM_BOUNDS.lnMin || ln > PARAM_BOUNDS.lnMax) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
if (r < PARAM_BOUNDS.rMin || r > PARAM_BOUNDS.rMax) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
if (p < PARAM_BOUNDS.pMin || p > PARAM_BOUNDS.pMax) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
return (128 * Math.pow(2, ln) * r) / (1024 * 1024) <= MAX_WORKING_SET_MIB;
|
|
317
|
+
}
|
|
318
|
+
/** @constant {number} Minimum stored salt length in bytes. @inner @private */
|
|
319
|
+
var MIN_STORED_SALT_BYTES = 8;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Normalise a password to Unicode NFC.
|
|
323
|
+
*
|
|
324
|
+
* The same accented character has more than one valid encoding — `é` as a
|
|
325
|
+
* single code point, or `e` followed by a combining acute. A keyboard, an
|
|
326
|
+
* operating system and a browser can each produce a different one, so a user
|
|
327
|
+
* who registers on one device and signs in from another otherwise submits
|
|
328
|
+
* bytes that will never match, and is locked out of their own account with no
|
|
329
|
+
* explanation. Normalising both sides of the comparison removes that.
|
|
330
|
+
*
|
|
331
|
+
* @param {string} password
|
|
332
|
+
* @returns {string}
|
|
333
|
+
* @inner
|
|
334
|
+
* @private
|
|
335
|
+
*/
|
|
336
|
+
function normalizePassword(password) {
|
|
337
|
+
return password.normalize('NFC');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Reject anything that is not a usable password string.
|
|
342
|
+
*
|
|
343
|
+
* The byte cap is applied AFTER normalisation, since normalising can change the
|
|
344
|
+
* encoded length.
|
|
345
|
+
*
|
|
346
|
+
* @param {*} password
|
|
347
|
+
* @returns {?Error} the error, or `null` when acceptable.
|
|
348
|
+
* @inner
|
|
349
|
+
* @private
|
|
350
|
+
*/
|
|
351
|
+
function checkPasswordInput(password) {
|
|
352
|
+
if (typeof password !== 'string') {
|
|
353
|
+
return new Error('[gina authn] password must be a string — got: ' + (password === null ? 'null' : typeof password));
|
|
354
|
+
}
|
|
355
|
+
if (password.length === 0) {
|
|
356
|
+
return new Error('[gina authn] password must not be empty');
|
|
357
|
+
}
|
|
358
|
+
if (Buffer.byteLength(normalizePassword(password), 'utf8') > MAX_PASSWORD_BYTES) {
|
|
359
|
+
return new Error('[gina authn] password exceeds ' + MAX_PASSWORD_BYTES + ' bytes — reject it in your form validation before hashing');
|
|
360
|
+
}
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* base64 without padding, for PHC fields.
|
|
366
|
+
*
|
|
367
|
+
* @param {Buffer} buf
|
|
368
|
+
* @returns {string}
|
|
369
|
+
* @inner
|
|
370
|
+
* @private
|
|
371
|
+
*/
|
|
372
|
+
function b64(buf) {
|
|
373
|
+
return buf.toString('base64').replace(/=+$/, '');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Hash a password, producing a self-describing PHC string.
|
|
378
|
+
*
|
|
379
|
+
* Asynchronous by construction — there is no synchronous variant, because the
|
|
380
|
+
* work is ~100 ms and a sync KDF on the request path blocks the event loop for
|
|
381
|
+
* every other connection.
|
|
382
|
+
*
|
|
383
|
+
* The returned string is what you persist. It embeds the algorithm, the cost
|
|
384
|
+
* parameters and the salt, so nothing else needs storing and the parameters can
|
|
385
|
+
* be raised later without invalidating it.
|
|
386
|
+
*
|
|
387
|
+
* @param {string} password - plaintext; 1..1024 bytes UTF-8.
|
|
388
|
+
* @param {object} [options] - overrides.
|
|
389
|
+
* @param {number} [options.ln=17] - base-2 log of scrypt N (14..20).
|
|
390
|
+
* @param {number} [options.r=8] - block size.
|
|
391
|
+
* @param {number} [options.p=1] - parallelism.
|
|
392
|
+
* @param {function} cb - `cb(err, phcString)`. `err.code` is
|
|
393
|
+
* `AUTHN_QUEUE_FULL` when the hashing queue is shedding load — respond 503.
|
|
394
|
+
* @returns {void}
|
|
395
|
+
* @memberof module:lib/authn
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* lib.authn.hashPassword('correct horse battery staple', function (err, hash) {
|
|
399
|
+
* // hash === '$scrypt$ln=17,r=8,p=1$<salt>$<key>' -> store it
|
|
400
|
+
* });
|
|
401
|
+
*
|
|
402
|
+
* @example <caption>Lower cost in a test suite</caption>
|
|
403
|
+
* lib.authn.hashPassword(pw, { ln: 12 }, function (err, hash) { });
|
|
404
|
+
*/
|
|
405
|
+
function hashPassword(password, options, cb) {
|
|
406
|
+
if (typeof options === 'function') {
|
|
407
|
+
cb = options;
|
|
408
|
+
options = {};
|
|
409
|
+
}
|
|
410
|
+
if (typeof cb !== 'function') {
|
|
411
|
+
throw new Error('[gina authn] hashPassword(password[, options], cb) requires a callback function');
|
|
412
|
+
}
|
|
413
|
+
options = options || {};
|
|
414
|
+
|
|
415
|
+
var inputErr = checkPasswordInput(password);
|
|
416
|
+
if (inputErr) {
|
|
417
|
+
return process.nextTick(function () { cb(inputErr); });
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
var ln = (typeof options.ln === 'number') ? options.ln : SCRYPT_PARAMS.ln;
|
|
421
|
+
var r = (typeof options.r === 'number') ? options.r : SCRYPT_PARAMS.r;
|
|
422
|
+
var p = (typeof options.p === 'number') ? options.p : SCRYPT_PARAMS.p;
|
|
423
|
+
|
|
424
|
+
// Exactly the bounds parseScryptPhc enforces. They were once looser here —
|
|
425
|
+
// `r` and `p` had no upper bound while the parser capped them — so
|
|
426
|
+
// hashPassword happily minted hashes verifyPassword could not read, and the
|
|
427
|
+
// CORRECT password then reported "wrong password" forever, with no error
|
|
428
|
+
// anywhere: a silent permanent lockout. Measured with r=64.
|
|
429
|
+
if (!costWithinBounds(ln, r, p)) {
|
|
430
|
+
return process.nextTick(function () {
|
|
431
|
+
cb(new Error('[gina authn] hashPassword: cost out of range — `ln` '
|
|
432
|
+
+ PARAM_BOUNDS.lnMin + '..' + PARAM_BOUNDS.lnMax
|
|
433
|
+
+ ', `r` ' + PARAM_BOUNDS.rMin + '..' + PARAM_BOUNDS.rMax
|
|
434
|
+
+ ', `p` ' + PARAM_BOUNDS.pMin + '..' + PARAM_BOUNDS.pMax
|
|
435
|
+
+ ', and 128 * 2^ln * r must not exceed ' + MAX_WORKING_SET_MIB + ' MiB'
|
|
436
|
+
+ ' — got: ln=' + JSON.stringify(ln) + ', r=' + JSON.stringify(r) + ', p=' + JSON.stringify(p)));
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
var salt = crypto.randomBytes(SALT_BYTES);
|
|
441
|
+
var N = Math.pow(2, ln);
|
|
442
|
+
var pw = normalizePassword(password);
|
|
443
|
+
|
|
444
|
+
withSlot(function (release) {
|
|
445
|
+
// node's default maxmem (32 MiB) is below what these parameters need, so
|
|
446
|
+
// it must be raised explicitly or scrypt throws. 128 * N * r is the
|
|
447
|
+
// documented working-set formula; the headroom multiplier covers node's
|
|
448
|
+
// own bookkeeping.
|
|
449
|
+
var maxmem = 128 * N * r * 2;
|
|
450
|
+
crypto.scrypt(pw, salt, KEY_BYTES, { N: N, r: r, p: p, maxmem: maxmem }, function (err, derived) {
|
|
451
|
+
release();
|
|
452
|
+
if (err) {
|
|
453
|
+
return cb(err);
|
|
454
|
+
}
|
|
455
|
+
cb(null, '$scrypt$ln=' + ln + ',r=' + r + ',p=' + p + '$' + b64(salt) + '$' + b64(derived));
|
|
456
|
+
});
|
|
457
|
+
}, cb);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Parse a gina scrypt PHC string.
|
|
462
|
+
*
|
|
463
|
+
* @param {string} stored
|
|
464
|
+
* @returns {?{ln: number, r: number, p: number, salt: Buffer, hash: Buffer}} `null` when the string is not a well-formed gina scrypt hash.
|
|
465
|
+
* @inner
|
|
466
|
+
* @private
|
|
467
|
+
*/
|
|
468
|
+
function parseScryptPhc(stored) {
|
|
469
|
+
if (typeof stored !== 'string') {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
var m = /^\$scrypt\$ln=(\d+),r=(\d+),p=(\d+)\$([A-Za-z0-9+/]+)\$([A-Za-z0-9+/]+)$/.exec(stored);
|
|
473
|
+
if (!m) {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
var ln = parseInt(m[1], 10);
|
|
477
|
+
var r = parseInt(m[2], 10);
|
|
478
|
+
var p = parseInt(m[3], 10);
|
|
479
|
+
// A stored hash is attacker-controlled the moment the credential store is.
|
|
480
|
+
// Unbounded parameters here would let a single verify allocate arbitrary
|
|
481
|
+
// memory — the same DoS the input cap closes from the other side.
|
|
482
|
+
if (!costWithinBounds(ln, r, p)) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
var salt = Buffer.from(m[4], 'base64');
|
|
486
|
+
var hash = Buffer.from(m[5], 'base64');
|
|
487
|
+
// Length floors, not merely non-emptiness: verifyPassword derives exactly
|
|
488
|
+
// `hash.length` bytes, so a short stored key shrinks the comparison to that
|
|
489
|
+
// width. Measured before this guard: a 1-byte key authenticated 1 of 60
|
|
490
|
+
// random passwords. We mint 32/16, so anything below these floors is
|
|
491
|
+
// corruption or forgery either way.
|
|
492
|
+
if (salt.length < MIN_STORED_SALT_BYTES || hash.length < MIN_STORED_KEY_BYTES) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
return { ln: ln, r: r, p: p, salt: salt, hash: hash };
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Resolve an optional verifier package from the consuming project.
|
|
500
|
+
*
|
|
501
|
+
* Same project-path resolution every gina connector uses for its driver — the
|
|
502
|
+
* package belongs to the bundle, not the framework.
|
|
503
|
+
*
|
|
504
|
+
* @param {string} pkg - `'argon2'` or `'bcrypt'`.
|
|
505
|
+
* @returns {?object} the module, or `null` when not installed.
|
|
506
|
+
* @inner
|
|
507
|
+
* @private
|
|
508
|
+
*/
|
|
509
|
+
function resolveVerifier(pkg) {
|
|
510
|
+
if (_injectedVerifiers[pkg]) {
|
|
511
|
+
return _injectedVerifiers[pkg];
|
|
512
|
+
}
|
|
513
|
+
try {
|
|
514
|
+
var p = _(getPath('project') + '/node_modules/' + pkg, true);
|
|
515
|
+
return require(p);
|
|
516
|
+
} catch (e) {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/** @type {object} Test-injected verifier modules. @inner @private */
|
|
522
|
+
var _injectedVerifiers = {};
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Inject a verifier module, bypassing project-path resolution.
|
|
526
|
+
*
|
|
527
|
+
* Test seam — mirrors `lib/metrics`' `opts.client`. Not part of the supported
|
|
528
|
+
* surface.
|
|
529
|
+
*
|
|
530
|
+
* @param {string} pkg - `'argon2'` or `'bcrypt'`.
|
|
531
|
+
* @param {?object} mod - the module, or `null` to clear.
|
|
532
|
+
* @returns {void}
|
|
533
|
+
* @memberof module:lib/authn
|
|
534
|
+
* @private
|
|
535
|
+
*/
|
|
536
|
+
function _setVerifier(pkg, mod) {
|
|
537
|
+
if (mod === null) {
|
|
538
|
+
delete _injectedVerifiers[pkg];
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
_injectedVerifiers[pkg] = mod;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Verify a password against a stored hash.
|
|
546
|
+
*
|
|
547
|
+
* Accepts gina's own `$scrypt$` hashes, and — through the consuming project's
|
|
548
|
+
* own `argon2` / `bcrypt` install — `$argon2i$` / `$argon2d$` / `$argon2id$`
|
|
549
|
+
* and `$2a$` / `$2b$` / `$2y$`. That foreign-format branch exists so a bundle
|
|
550
|
+
* arriving with existing hashes keeps working; pair it with {@link needsRehash}
|
|
551
|
+
* to migrate on login.
|
|
552
|
+
*
|
|
553
|
+
* **Never throws for a bad password.** A malformed or unrecognised stored hash
|
|
554
|
+
* is `cb(null, false)`, not an error — a corrupted record must not be
|
|
555
|
+
* distinguishable from a wrong password by an attacker watching responses. A
|
|
556
|
+
* genuine operational failure (KDF error, verifier package missing for a format
|
|
557
|
+
* that needs one) IS an error, because it needs an operator, not a login form.
|
|
558
|
+
*
|
|
559
|
+
* @param {string} password - plaintext to check.
|
|
560
|
+
* @param {string} stored - the persisted hash string.
|
|
561
|
+
* @param {function} cb - `cb(err, isValid)`. `err.code` is
|
|
562
|
+
* `AUTHN_QUEUE_FULL` when the hashing queue is shedding load — respond 503,
|
|
563
|
+
* and treat the account-not-found branch identically (see {@link dummyVerify}).
|
|
564
|
+
* @returns {void}
|
|
565
|
+
* @memberof module:lib/authn
|
|
566
|
+
*
|
|
567
|
+
* @example
|
|
568
|
+
* lib.authn.verifyPassword(submitted, account.passwordHash, function (err, ok) {
|
|
569
|
+
* if (err) { return self.throwError(500); } // operational
|
|
570
|
+
* if (!ok) { return deny(); } // wrong password
|
|
571
|
+
* });
|
|
572
|
+
*
|
|
573
|
+
* @example <caption>Unknown account — spend the same time (see dummyVerify)</caption>
|
|
574
|
+
* if (!account) {
|
|
575
|
+
* return lib.authn.dummyVerify(submitted, { like: referenceHash }, function () { deny(); });
|
|
576
|
+
* }
|
|
577
|
+
*/
|
|
578
|
+
function verifyPassword(password, stored, cb) {
|
|
579
|
+
if (typeof cb !== 'function') {
|
|
580
|
+
throw new Error('[gina authn] verifyPassword(password, stored, cb) requires a callback function');
|
|
581
|
+
}
|
|
582
|
+
var inputErr = checkPasswordInput(password);
|
|
583
|
+
if (inputErr) {
|
|
584
|
+
return process.nextTick(function () { cb(inputErr); });
|
|
585
|
+
}
|
|
586
|
+
if (typeof stored !== 'string' || stored.length === 0) {
|
|
587
|
+
return process.nextTick(function () { cb(null, false); });
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (/^\$argon2[id]{1,2}\$/.test(stored)) {
|
|
591
|
+
var argon2 = resolveVerifier('argon2');
|
|
592
|
+
if (!argon2 || typeof argon2.verify !== 'function') {
|
|
593
|
+
return process.nextTick(function () {
|
|
594
|
+
cb(new Error('[gina authn] this hash is argon2, which gina verifies through your project\'s own package. Run: npm install argon2'));
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
return Promise.resolve()
|
|
598
|
+
.then(function () { return argon2.verify(stored, normalizePassword(password)); })
|
|
599
|
+
.then(function (ok) { cb(null, ok === true); })
|
|
600
|
+
.catch(function () { cb(null, false); });
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (/^\$2[aby]\$/.test(stored)) {
|
|
604
|
+
var bcrypt = resolveVerifier('bcrypt');
|
|
605
|
+
if (!bcrypt || typeof bcrypt.compare !== 'function') {
|
|
606
|
+
return process.nextTick(function () {
|
|
607
|
+
cb(new Error('[gina authn] this hash is bcrypt, which gina verifies through your project\'s own package. Run: npm install bcrypt'));
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
return bcrypt.compare(normalizePassword(password), stored, function (err, ok) {
|
|
611
|
+
if (err) {
|
|
612
|
+
return cb(null, false);
|
|
613
|
+
}
|
|
614
|
+
cb(null, ok === true);
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
var parsed = parseScryptPhc(stored);
|
|
619
|
+
if (!parsed) {
|
|
620
|
+
return process.nextTick(function () { cb(null, false); });
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
var N = Math.pow(2, parsed.ln);
|
|
624
|
+
var pwNorm = normalizePassword(password);
|
|
625
|
+
withSlot(function (release) {
|
|
626
|
+
var maxmem = 128 * N * parsed.r * 2;
|
|
627
|
+
crypto.scrypt(pwNorm, parsed.salt, parsed.hash.length, { N: N, r: parsed.r, p: parsed.p, maxmem: maxmem }, function (err, derived) {
|
|
628
|
+
release();
|
|
629
|
+
if (err) {
|
|
630
|
+
return cb(err);
|
|
631
|
+
}
|
|
632
|
+
// Lengths match by construction (derived is requested at the stored
|
|
633
|
+
// length), so timingSafeEqual cannot throw here — the try/catch is
|
|
634
|
+
// the house convention, and it keeps a future refactor fail-closed.
|
|
635
|
+
var ok = false;
|
|
636
|
+
try {
|
|
637
|
+
ok = crypto.timingSafeEqual(derived, parsed.hash);
|
|
638
|
+
} catch (e) {
|
|
639
|
+
ok = false;
|
|
640
|
+
}
|
|
641
|
+
cb(null, ok);
|
|
642
|
+
});
|
|
643
|
+
}, cb);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Spend a verify's worth of time without a stored hash.
|
|
648
|
+
*
|
|
649
|
+
* A login that returns instantly for an unknown account and slowly for a known
|
|
650
|
+
* one is a user-enumeration oracle, and it is measurable across a network. Call
|
|
651
|
+
* this on the account-not-found branch so both branches cost the same.
|
|
652
|
+
*
|
|
653
|
+
* **The cost must match the cost your stored hashes actually use**, and by
|
|
654
|
+
* default that is the shipped parameters. If your hashes were minted cheaper —
|
|
655
|
+
* a lowered `ln`, or legacy hashes not yet migrated by {@link needsRehash} —
|
|
656
|
+
* pass the same parameters, or pass `like: <a stored hash>` and the cost is read
|
|
657
|
+
* from it. Getting this wrong does not merely fail to close the oracle, it
|
|
658
|
+
* INVERTS it: measured at defaults against `ln=14` hashes, the unknown-account
|
|
659
|
+
* branch ran 13.9x SLOWER than the known-account one, which is a louder signal
|
|
660
|
+
* than doing nothing.
|
|
661
|
+
*
|
|
662
|
+
* @param {string} password - the submitted plaintext.
|
|
663
|
+
* @param {object|function} [options] - cost overrides, or the callback.
|
|
664
|
+
* @param {string} [options.like] - a stored hash to copy the cost from (wins over ln/r/p).
|
|
665
|
+
* @param {number} [options.ln=17]
|
|
666
|
+
* @param {number} [options.r=8]
|
|
667
|
+
* @param {number} [options.p=1]
|
|
668
|
+
* @param {function} cb - `cb(err)`. `err` is `null` normally; it
|
|
669
|
+
* carries `AUTHN_QUEUE_FULL` when the hashing queue is shedding load. Handle
|
|
670
|
+
* it EXACTLY as you handle {@link verifyPassword}'s error — respond 503 —
|
|
671
|
+
* because any divergence between the two branches under saturation is the
|
|
672
|
+
* enumeration signal this function exists to remove.
|
|
673
|
+
* @returns {void}
|
|
674
|
+
* @memberof module:lib/authn
|
|
675
|
+
*
|
|
676
|
+
* @example <caption>Always pass a cost — `like` reads it from a real hash</caption>
|
|
677
|
+
* if (!account) {
|
|
678
|
+
* // `referenceHash` is any hash from your store (seed one at install time).
|
|
679
|
+
* // Reading the cost from real data is what keeps the two branches matched
|
|
680
|
+
* // as you raise parameters — a separate setting silently drifts.
|
|
681
|
+
* return lib.authn.dummyVerify(self.post.password, { like: referenceHash }, function () {
|
|
682
|
+
* self.renderJSON({ error: 'invalid credentials' });
|
|
683
|
+
* });
|
|
684
|
+
* }
|
|
685
|
+
*
|
|
686
|
+
* @example <caption>Or state the cost explicitly</caption>
|
|
687
|
+
* lib.authn.dummyVerify(submitted, { ln: 14 }, function () { deny(); });
|
|
688
|
+
*/
|
|
689
|
+
function dummyVerify(password, options, cb) {
|
|
690
|
+
if (typeof options === 'function') {
|
|
691
|
+
cb = options;
|
|
692
|
+
options = {};
|
|
693
|
+
}
|
|
694
|
+
if (typeof cb !== 'function') {
|
|
695
|
+
throw new Error('[gina authn] dummyVerify(password[, options], cb) requires a callback function');
|
|
696
|
+
}
|
|
697
|
+
options = options || {};
|
|
698
|
+
|
|
699
|
+
var ln = SCRYPT_PARAMS.ln;
|
|
700
|
+
var r = SCRYPT_PARAMS.r;
|
|
701
|
+
var p = SCRYPT_PARAMS.p;
|
|
702
|
+
|
|
703
|
+
// `like` copies the cost off a real stored hash — the most reliable way to
|
|
704
|
+
// stay matched, since it needs no separate configuration to keep in sync.
|
|
705
|
+
if (typeof options.like === 'string') {
|
|
706
|
+
var ref = parseScryptPhc(options.like);
|
|
707
|
+
if (ref) {
|
|
708
|
+
ln = ref.ln;
|
|
709
|
+
r = ref.r;
|
|
710
|
+
p = ref.p;
|
|
711
|
+
}
|
|
712
|
+
} else {
|
|
713
|
+
if (typeof options.ln === 'number') { ln = options.ln; }
|
|
714
|
+
if (typeof options.r === 'number') { r = options.r; }
|
|
715
|
+
if (typeof options.p === 'number') { p = options.p; }
|
|
716
|
+
}
|
|
717
|
+
// Silently clamp rather than throw: this runs on a failed-login path, and an
|
|
718
|
+
// exception there would turn a mistuned parameter into an outage.
|
|
719
|
+
if (!isFinite(ln) || ln < 14 || ln > 20) { ln = SCRYPT_PARAMS.ln; }
|
|
720
|
+
if (!isFinite(r) || r < 1 || r > 32) { r = SCRYPT_PARAMS.r; }
|
|
721
|
+
if (!isFinite(p) || p < 1 || p > 16) { p = SCRYPT_PARAMS.p; }
|
|
722
|
+
|
|
723
|
+
var pw = (typeof password === 'string' && password.length > 0 && Buffer.byteLength(password, 'utf8') <= MAX_PASSWORD_BYTES)
|
|
724
|
+
? normalizePassword(password)
|
|
725
|
+
: 'x';
|
|
726
|
+
var salt = crypto.randomBytes(SALT_BYTES);
|
|
727
|
+
var N = Math.pow(2, ln);
|
|
728
|
+
withSlot(function (release) {
|
|
729
|
+
var maxmem = 128 * N * r * 2;
|
|
730
|
+
crypto.scrypt(pw, salt, KEY_BYTES, { N: N, r: r, p: p, maxmem: maxmem }, function () {
|
|
731
|
+
release();
|
|
732
|
+
cb(null);
|
|
733
|
+
});
|
|
734
|
+
}, function (err) {
|
|
735
|
+
// Surface the shed error, because verifyPassword surfaces it too. If
|
|
736
|
+
// this branch swallowed it, then under saturation a known account would
|
|
737
|
+
// 500 while an unknown one returned "invalid credentials" — saturation
|
|
738
|
+
// itself would become the enumeration signal this function exists to
|
|
739
|
+
// remove. Both branches must look identical in every regime.
|
|
740
|
+
cb(err);
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Report whether a stored hash is below the current policy.
|
|
746
|
+
*
|
|
747
|
+
* True when the hash is not gina scrypt (a foreign argon2 / bcrypt hash, or an
|
|
748
|
+
* unparseable string) or when its encoded parameters are weaker than the
|
|
749
|
+
* current defaults. Re-hash on the next successful login — the one moment the
|
|
750
|
+
* plaintext is available.
|
|
751
|
+
*
|
|
752
|
+
* @param {string} stored - the persisted hash string.
|
|
753
|
+
* @returns {boolean}
|
|
754
|
+
* @memberof module:lib/authn
|
|
755
|
+
*
|
|
756
|
+
* @example
|
|
757
|
+
* if (ok && lib.authn.needsRehash(account.passwordHash)) {
|
|
758
|
+
* lib.authn.hashPassword(submitted, function (err, fresh) {
|
|
759
|
+
* if (!err) { account.passwordHash = fresh; account.save(); }
|
|
760
|
+
* });
|
|
761
|
+
* }
|
|
762
|
+
*/
|
|
763
|
+
function needsRehash(stored) {
|
|
764
|
+
var parsed = parseScryptPhc(stored);
|
|
765
|
+
if (!parsed) {
|
|
766
|
+
return true;
|
|
767
|
+
}
|
|
768
|
+
// A key shorter than we mint verifies at reduced width forever otherwise —
|
|
769
|
+
// a record truncated by, say, a bcrypt-sized VARCHAR(60) parses (it clears
|
|
770
|
+
// the 16-byte floor) and would never be flagged for migration.
|
|
771
|
+
return parsed.hash.length !== KEY_BYTES
|
|
772
|
+
|| parsed.ln < SCRYPT_PARAMS.ln
|
|
773
|
+
|| parsed.r < SCRYPT_PARAMS.r
|
|
774
|
+
|| parsed.p < SCRYPT_PARAMS.p;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Validate a password against a length / composition policy.
|
|
779
|
+
*
|
|
780
|
+
* Length-first, per NIST SP 800-63B: the default is a 12-character minimum with
|
|
781
|
+
* NO character-class requirements, because mandatory composition measurably
|
|
782
|
+
* degrades real-world password quality. Composition rules are available for
|
|
783
|
+
* consumers whose auditor requires them.
|
|
784
|
+
*
|
|
785
|
+
* @param {string} password - the candidate.
|
|
786
|
+
* @param {object} [options] - policy overrides.
|
|
787
|
+
* @param {number} [options.minLength=12] - minimum characters (code points, not bytes).
|
|
788
|
+
* @param {number} [options.maxLength=1024] - maximum bytes of UTF-8.
|
|
789
|
+
* @param {boolean} [options.requireUppercase=false]
|
|
790
|
+
* @param {boolean} [options.requireLowercase=false]
|
|
791
|
+
* @param {boolean} [options.requireDigit=false]
|
|
792
|
+
* @param {boolean} [options.requireSymbol=false]
|
|
793
|
+
* @param {string[]} [options.deny=[]] - case-insensitive substrings to reject (the user's own email, the site name, …).
|
|
794
|
+
* @returns {{valid: boolean, errors: string[]}} `errors` are stable machine-readable codes: `too-short`, `too-long`, `missing-uppercase`, `missing-lowercase`, `missing-digit`, `missing-symbol`, `denied-substring`, `not-a-string`.
|
|
795
|
+
* @memberof module:lib/authn
|
|
796
|
+
*
|
|
797
|
+
* @example
|
|
798
|
+
* var check = lib.authn.validatePasswordPolicy(self.post.password, {
|
|
799
|
+
* deny: [ self.post.email, 'gina' ]
|
|
800
|
+
* });
|
|
801
|
+
* if (!check.valid) { return self.renderJSON({ errors: check.errors }); }
|
|
802
|
+
*/
|
|
803
|
+
function validatePasswordPolicy(password, options) {
|
|
804
|
+
options = options || {};
|
|
805
|
+
var errors = [];
|
|
806
|
+
|
|
807
|
+
if (typeof password !== 'string') {
|
|
808
|
+
return { valid: false, errors: [ 'not-a-string' ] };
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Validated rather than trusted: `typeof NaN === 'number'`, so a
|
|
812
|
+
// `parseInt` of a missing config key produced minLength NaN, every
|
|
813
|
+
// comparison against it went false, and an EMPTY password passed the policy.
|
|
814
|
+
// A negative minLength did the same. This is the one input in the module
|
|
815
|
+
// that was taken on faith, and it fails OPEN.
|
|
816
|
+
var minLength = DEFAULT_MIN_LENGTH;
|
|
817
|
+
if (typeof options.minLength !== 'undefined') {
|
|
818
|
+
if (typeof options.minLength !== 'number' || !isFinite(options.minLength)
|
|
819
|
+
|| options.minLength < 0 || Math.floor(options.minLength) !== options.minLength) {
|
|
820
|
+
throw new Error('[gina authn] validatePasswordPolicy: `minLength` must be a whole number >= 0 — got: ' + JSON.stringify(options.minLength));
|
|
821
|
+
}
|
|
822
|
+
minLength = options.minLength;
|
|
823
|
+
}
|
|
824
|
+
var maxLength = MAX_PASSWORD_BYTES;
|
|
825
|
+
if (typeof options.maxLength !== 'undefined') {
|
|
826
|
+
if (typeof options.maxLength !== 'number' || !isFinite(options.maxLength)
|
|
827
|
+
|| options.maxLength < 1 || Math.floor(options.maxLength) !== options.maxLength) {
|
|
828
|
+
throw new Error('[gina authn] validatePasswordPolicy: `maxLength` must be a whole number >= 1 — got: ' + JSON.stringify(options.maxLength));
|
|
829
|
+
}
|
|
830
|
+
maxLength = options.maxLength;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// Byte length FIRST, and return on breach: `Array.from` materialises an
|
|
834
|
+
// array of code points, so measuring length before enforcing the cap meant
|
|
835
|
+
// a 16 MiB submission allocated ~144 MiB just to conclude "too long".
|
|
836
|
+
if (Buffer.byteLength(password, 'utf8') > maxLength) {
|
|
837
|
+
return { valid: false, errors: [ 'too-long' ] };
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Count code points, not UTF-16 units, so an emoji or an astral character
|
|
841
|
+
// counts once rather than twice. NOTE this counts CODE POINTS, not grapheme
|
|
842
|
+
// clusters: a family emoji is one perceived character but several code
|
|
843
|
+
// points, so `minLength` is a floor on code points and can overstate
|
|
844
|
+
// strength for grapheme-rich input.
|
|
845
|
+
var length = Array.from(password).length;
|
|
846
|
+
if (length < minLength) {
|
|
847
|
+
errors.push('too-short');
|
|
848
|
+
}
|
|
849
|
+
if (options.requireUppercase === true && !/[A-Z]/.test(password)) {
|
|
850
|
+
errors.push('missing-uppercase');
|
|
851
|
+
}
|
|
852
|
+
if (options.requireLowercase === true && !/[a-z]/.test(password)) {
|
|
853
|
+
errors.push('missing-lowercase');
|
|
854
|
+
}
|
|
855
|
+
if (options.requireDigit === true && !/[0-9]/.test(password)) {
|
|
856
|
+
errors.push('missing-digit');
|
|
857
|
+
}
|
|
858
|
+
if (options.requireSymbol === true && !/[^A-Za-z0-9]/.test(password)) {
|
|
859
|
+
errors.push('missing-symbol');
|
|
860
|
+
}
|
|
861
|
+
if (Array.isArray(options.deny) && options.deny.length > 0) {
|
|
862
|
+
var lower = password.toLowerCase();
|
|
863
|
+
for (var i = 0; i < options.deny.length; i++) {
|
|
864
|
+
var needle = options.deny[i];
|
|
865
|
+
if (typeof needle === 'string' && needle.length > 0 && lower.indexOf(needle.toLowerCase()) > -1) {
|
|
866
|
+
errors.push('denied-substring');
|
|
867
|
+
break;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
return { valid: errors.length === 0, errors: errors };
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
module.exports = {
|
|
876
|
+
hashPassword : hashPassword,
|
|
877
|
+
verifyPassword : verifyPassword,
|
|
878
|
+
dummyVerify : dummyVerify,
|
|
879
|
+
needsRehash : needsRehash,
|
|
880
|
+
validatePasswordPolicy : validatePasswordPolicy,
|
|
881
|
+
setMaxConcurrentHashes : setMaxConcurrentHashes,
|
|
882
|
+
createLockout : lockout.createLockout,
|
|
883
|
+
generateTotpSecret : totp.generateTotpSecret,
|
|
884
|
+
generateTotp : totp.generateTotp,
|
|
885
|
+
verifyTotp : totp.verifyTotp,
|
|
886
|
+
otpauthURL : totp.otpauthURL,
|
|
887
|
+
// test seams — may change without notice
|
|
888
|
+
_base32Encode : totp._base32Encode,
|
|
889
|
+
_base32Decode : totp._base32Decode,
|
|
890
|
+
_hotp : totp._hotp,
|
|
891
|
+
_createMemoryStore : lockout._createMemoryStore,
|
|
892
|
+
_DEFAULT_MAX_ATTEMPTS : lockout._DEFAULT_MAX_ATTEMPTS,
|
|
893
|
+
_DEFAULT_LOCK_MS : lockout._DEFAULT_LOCK_MS,
|
|
894
|
+
_parseScryptPhc : parseScryptPhc,
|
|
895
|
+
_setVerifier : _setVerifier,
|
|
896
|
+
_SCRYPT_PARAMS : SCRYPT_PARAMS,
|
|
897
|
+
_MAX_PASSWORD_BYTES : MAX_PASSWORD_BYTES,
|
|
898
|
+
_DEFAULT_MIN_LENGTH : DEFAULT_MIN_LENGTH
|
|
899
|
+
};
|