diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 0000000000000000000000000000000000000000..2645e9e38579fa27dbb608f92bfad81d8c315d89 --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,25 @@ +version = 1 +xclude_patterns = [ + "dist/**", + "**/node_modules/", + "js/**/*.min.js", + "backend/**/*" +] + +[[analyzers]] +name = "javascript" +enabled = true + +[analyzers.meta] +environment = ["mongo"] +plugins = ["react"] +skip_doc_coverage = ["class-expression", "method-definition"] +dependency_file_paths = [ + "./frontend/", + "./" +] +dialect = "typescript" + +[[transformers]] +name = "prettier" +enabled = false diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000000000000000000000000000000000..b7c10208db6fc4021895021b9715e6569c4228ca --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +CWE_MODEL_URL=https://drive.usercontent.google.com/download?id=1OtRNObv-Il2B5nDnpzMSGj_yBJAlskuS&export=download&confirm=``` +CVSS_MODEL_URL=https://drive.usercontent.google.com/download?id=1nS1lQpVVJ431wUyVSs5_Srega6QVPyc8&export=download&confirm= \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..d22cef17abd65c6b9edcc26dfb84d6ae5fe6c6ac 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +*.gif filter=lfs diff=lfs merge=lfs -text diff --git a/.gitconfig b/.gitconfig new file mode 100644 index 0000000000000000000000000000000000000000..41e358c680b809600e1d5465f63ee78e2d63e48f --- /dev/null +++ b/.gitconfig @@ -0,0 +1,2 @@ +[pull] + rebase = yes diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..91e56eca994d1208c6a2c105f4bbbcdc02ccd053 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +.DS_Store +.thumbs.db +node_modules +/dist +npm-debug.log* +yarn-debug.log* +yarn-error.log* +mongo-data* +.quasar +.venv +package-lock.json +.env + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +.dccache +.sourcery.yaml + +# Version managers +.tool-versions + + +# modelo +cwe_api/modelo_cwe +cwe_api/utils diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000000000000000000000000000000000..1385696fe3c142faed20d82952680ba521c49878 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2024 Camilo Vera Vidales + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..2dc290952d99f36fd7d09cdd4a1a1e37c1a42914 --- /dev/null +++ b/app.py @@ -0,0 +1,7 @@ +from fastapi import FastAPI + +app = FastAPI() + +@app.get("/") +def greet_json(): + return {"Hello": "World!"} \ No newline at end of file diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..a2df488f2ad96ca8db291fdc4cd954e6ecf2f34f --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,3 @@ +node_modules +mongo-data +mongo-data-dev diff --git a/backend/.prettierrc b/backend/.prettierrc new file mode 100644 index 0000000000000000000000000000000000000000..83c0c9f23f4fbf2f5ef66ee0dfed5d4e24d581ab --- /dev/null +++ b/backend/.prettierrc @@ -0,0 +1,12 @@ +{ + "arrowParens": "avoid", + "singleQuote": true, + "overrides": [ + { + "files": ".changeset/**/*", + "options": { + "singleQuote": false + } + } + ] +} diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..f9d3e6c9f4f7a17bf734d1b2784fb20591094455 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,12 @@ +FROM node:lts-alpine + +RUN mkdir -p /app +WORKDIR /app +COPY package*.json ./ +RUN apk --no-cache add --virtual builds-deps build-base python3 git libreoffice ttf-liberation +RUN npm install +COPY . . +EXPOSE 4242 +ENV NODE_ENV prod +ENV NODE_ICU_DATA=node_modules/full-icu +ENTRYPOINT ["npm", "start"] diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev new file mode 100644 index 0000000000000000000000000000000000000000..af9fad4f7ac51ab19f865a59439cea741dbeeac9 --- /dev/null +++ b/backend/Dockerfile.dev @@ -0,0 +1,11 @@ +FROM node:lts-alpine + +RUN mkdir -p /app +WORKDIR /app +COPY package*.json ./ +RUN apk --no-cache add --virtual builds-deps build-base python3 git libreoffice ttf-liberation +RUN npm install +COPY . . +ENV NODE_ENV dev +ENV NODE_ICU_DATA=node_modules/full-icu +ENTRYPOINT [ "npm", "run", "dev"] \ No newline at end of file diff --git a/backend/Dockerfile.test b/backend/Dockerfile.test new file mode 100644 index 0000000000000000000000000000000000000000..5cc4146dc02ecae7b038ff2b0cec864b84bae889 --- /dev/null +++ b/backend/Dockerfile.test @@ -0,0 +1,12 @@ +FROM node:lts-alpine + +RUN mkdir -p /app +WORKDIR /app +COPY package*.json ./ +RUN apk --no-cache add --virtual builds-deps build-base python3 git libreoffice ttf-liberation +RUN npm install +COPY . . +ENV NODE_ENV test +ENV NODE_ICU_DATA=node_modules/full-icu +RUN npm install +ENTRYPOINT ["npm", "run", "test"] \ No newline at end of file diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2f98d45ede1d3bead0f522227e956b4611293dc8 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,21 @@ +# Installation for developpment environnment + +*Source code can be modified live and application will automatically reload on changes.* + +Build and run Docker containers +``` +docker-compose -f ./docker-compose.dev.yml up -d --build +``` + +Display container logs +``` +docker-compose logs -f +``` + +Stop/Start container +``` +docker-compose stop +docker-compose start +``` + +API is accessible through https://localhost:5252/api \ No newline at end of file diff --git a/backend/babel.config.js b/backend/babel.config.js new file mode 100644 index 0000000000000000000000000000000000000000..f037a1a2a1477424dfca78799d57f863a5b74f81 --- /dev/null +++ b/backend/babel.config.js @@ -0,0 +1,12 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: 'current', + }, + }, + ], + ], +}; diff --git a/backend/docker-compose.dev.yml b/backend/docker-compose.dev.yml new file mode 100644 index 0000000000000000000000000000000000000000..279ac2f311ec0bde2f759b484a6e8bde65b46db2 --- /dev/null +++ b/backend/docker-compose.dev.yml @@ -0,0 +1,41 @@ +version: '3' +services: + mongodb: + image: mongo:4.2.15 + container_name: mongo-auditforge-dev + volumes: + - ./mongo-data-dev:/data/db + restart: always + ports: + - 127.0.0.1:27017:27017 + environment: + - MONGO_DB:auditforge + networks: + - backend + + auditforge-backend: + build: + context: . + dockerfile: Dockerfile.dev + image: auditforge:backend-dev + container_name: auditforge-backend-dev + volumes: + - ./src:/app/src + - ./ssl:/app/ssl + - ./report-templates:/app/report-templates + depends_on: + - mongodb + restart: always + ports: + - 5252:5252 + links: + - mongodb + networks: + - backend + +volumes: + mongo-data-dev: + +networks: + backend: + driver: bridge diff --git a/backend/docker-compose.test.yml b/backend/docker-compose.test.yml new file mode 100644 index 0000000000000000000000000000000000000000..4ddc022bc991b94d4ef609c1e6375e2a383128bf --- /dev/null +++ b/backend/docker-compose.test.yml @@ -0,0 +1,30 @@ +version: '3' +services: + mongodb-test: + image: mongo:4.2.15 + container_name: mongo-auditforge-test + volumes: + - ./mongo-data-test:/data/db + restart: always + environment: + - MONGO_DB:auditforge + network_mode: host + + backend-test: + image: auditforge:backend-test + build: + context: . + dockerfile: Dockerfile.test + container_name: auditforge-backend-test + volumes: + - ./tests:/app/tests + - ./src:/app/src + - ./jest.config.js:/app/jest.config.js + environment: + API_URL: https://localhost:4242 + depends_on: + - mongodb-test + network_mode: host + +volumes: + mongo-data-test: diff --git a/backend/jest.config.js b/backend/jest.config.js new file mode 100644 index 0000000000000000000000000000000000000000..3913dcf54177bda8714a17a25c25b3e92bea40ff --- /dev/null +++ b/backend/jest.config.js @@ -0,0 +1,4 @@ +module.exports = { + testEnvironment: 'node', + verbose: true, +}; diff --git a/backend/report-templates/.gitignore b/backend/report-templates/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..ebebac81e6f4f7460d211346eef6b03dede8f3ac --- /dev/null +++ b/backend/report-templates/.gitignore @@ -0,0 +1 @@ +*.docx \ No newline at end of file diff --git a/backend/report-templates/.gitkeep b/backend/report-templates/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/backend/src/app.js b/backend/src/app.js new file mode 100644 index 0000000000000000000000000000000000000000..527e03eb774a1c0438dcd6b6aa41310ae6be77f9 --- /dev/null +++ b/backend/src/app.js @@ -0,0 +1,170 @@ +var fs = require('fs'); +var app = require('express')(); + +var https = require('https').Server( + { + key: fs.readFileSync(__dirname + '/../ssl/server.key'), + cert: fs.readFileSync(__dirname + '/../ssl/server.cert'), + + // TLS Versions + maxVersion: 'TLSv1.3', + minVersion: 'TLSv1.2', + + // Hardened configuration + ciphers: + 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384', + + honorCipherOrder: false, + }, + app, +); +app.disable('x-powered-by'); + +var io = require('socket.io')(https, { + cors: { + origin: '*', + }, +}); +var bodyParser = require('body-parser'); +var cookieParser = require('cookie-parser'); +var utils = require('./lib/utils'); + +// Get configuration +var env = process.env.NODE_ENV || 'dev'; +var config = require('./config/config.json')[env]; +global.__basedir = __dirname; + +// Database connection +var mongoose = require('mongoose'); +// Use native promises +mongoose.Promise = global.Promise; +// Trim all Strings +mongoose.Schema.Types.String.set('trim', true); + +mongoose.connect( + `mongodb://${config.database.server}:${config.database.port}/${config.database.name}`, + {}, +); + +// Models import +require('./models/user'); +require('./models/audit'); +require('./models/client'); +require('./models/company'); +require('./models/template'); +require('./models/vulnerability'); +require('./models/vulnerability-update'); +require('./models/language'); +require('./models/audit-type'); +require('./models/vulnerability-type'); +require('./models/vulnerability-category'); +require('./models/custom-section'); +require('./models/custom-field'); +require('./models/image'); +require('./models/settings'); + +// Socket IO configuration +io.on('connection', socket => { + socket.on('join', data => { + console.log( + `user ${data.username.replace(/\n|\r/g, '')} joined room ${data.room.replace(/\n|\r/g, '')}`, + ); + socket.username = data.username; + do { + socket.color = + '#' + (0x1000000 + Math.random() * 0xffffff).toString(16).substr(1, 6); + } while (socket.color === '#77c84e'); + socket.join(data.room); + io.to(data.room).emit('updateUsers'); + }); + socket.on('leave', data => { + console.log( + `user ${data.username.replace(/\n|\r/g, '')} left room ${data.room.replace(/\n|\r/g, '')}`, + ); + socket.leave(data.room); + io.to(data.room).emit('updateUsers'); + }); + socket.on('updateUsers', data => { + var userList = [ + ...new Set( + utils.getSockets(io, data.room).map(s => { + var user = {}; + user.username = s.username; + user.color = s.color; + user.menu = s.menu; + if (s.finding) user.finding = s.finding; + if (s.section) user.section = s.section; + return user; + }), + ), + ]; + io.to(data.room).emit('roomUsers', userList); + }); + socket.on('menu', data => { + socket.menu = data.menu; + data.finding ? (socket.finding = data.finding) : delete socket.finding; + data.section ? (socket.section = data.section) : delete socket.section; + io.to(data.room).emit('updateUsers'); + }); + socket.on('disconnect', () => { + socket.broadcast.emit('updateUsers'); + }); +}); + +// CORS +app.use(function (req, res, next) { + res.header('Access-Control-Allow-Origin', req.headers.origin); + res.header('Access-Control-Allow-Methods', 'GET,POST,DELETE,PUT,OPTIONS'); + res.header( + 'Access-Control-Allow-Headers', + 'Origin, X-Requested-With, Content-Type, Accept', + ); + res.header('Access-Control-Expose-Headers', 'Content-Disposition'); + res.header('Access-Control-Allow-Credentials', 'true'); + next(); +}); + +// CSP +app.use(function (req, res, next) { + res.header( + 'Content-Security-Policy', + "default-src 'none'; form-action 'none'; base-uri 'self'; frame-ancestors 'none'; sandbox; require-trusted-types-for 'script';", + ); + next(); +}); + +app.use(bodyParser.json({ limit: '100mb' })); +app.use( + bodyParser.urlencoded({ + limit: '10mb', + extended: false, // do not need to take care about images, videos -> false: only strings + }), +); + +app.use(cookieParser()); + +// Routes import +require('./routes/user')(app); +require('./routes/audit')(app, io); +require('./routes/client')(app); +require('./routes/company')(app); +require('./routes/vulnerability')(app); +require('./routes/template')(app); +require('./routes/vulnerability')(app); +require('./routes/data')(app); +require('./routes/image')(app); +require('./routes/settings')(app); +require('./routes/cwe')(app); +require('./routes/cvss')(app); +require('./routes/check-cwe-update')(app); +require('./routes/update-cwe-model')(app); + +app.get('*', function (req, res) { + res.status(404).json({ status: 'error', data: 'Route undefined' }); +}); + +// Start server + +https.listen(config.port, config.host); + +module.exports = app; diff --git a/backend/src/config/config-cwe.json b/backend/src/config/config-cwe.json new file mode 100644 index 0000000000000000000000000000000000000000..27bace4992ae6e71c0612dd13b973df94e9356cc --- /dev/null +++ b/backend/src/config/config-cwe.json @@ -0,0 +1,12 @@ +{ + "cwe-container": { + "host": "auditforge-cwe-api", + "port": 8000, + "check_timeout_ms": 30000, + "update_timeout_ms": 120000, + "endpoints": { + "check_update_endpoint": "check_cwe_update", + "update_cwe_endpoint": "update_cwe_model" + } + } +} diff --git a/backend/src/config/config.json b/backend/src/config/config.json new file mode 100644 index 0000000000000000000000000000000000000000..f03617743bb6a50683d181a3f31c2bcf038c1ea7 --- /dev/null +++ b/backend/src/config/config.json @@ -0,0 +1,33 @@ +{ + "dev": { + "port": 5252, + "host": "", + "database": { + "name": "auditforge", + "server": "mongo-auditforge-dev", + "port": "27017" + }, + "jwtSecret": "1a039133523dfca9cd5d0ac385c16f0f061558a96dc57384854ef90ed53e86dd", + "jwtRefreshSecret": "95b0c96984c301d94b41c3d2306fd041a001c58516eb5a23f83044822f42e558" + }, + "prod": { + "port": 4242, + "host": "", + "database": { + "name": "auditforge", + "server": "mongo-auditforge", + "port": "27017" + }, + "jwtSecret": "8565a16daedc531581393a08812af3c9043e702069216c54bd51c6613bcf9811", + "jwtRefreshSecret": "54f27d78990b5f4537702dbf97d9d746c7cb8172f070a1212933c877e8fc98a8" + }, + "test": { + "port": 6262, + "host": "", + "database": { + "name": "auditforge", + "server": "127.0.0.1", + "port": "27017" + } + } +} diff --git a/backend/src/config/roles.json b/backend/src/config/roles.json new file mode 100644 index 0000000000000000000000000000000000000000..56406ab8e2716b3f329e9205352d9444bcbb08a4 --- /dev/null +++ b/backend/src/config/roles.json @@ -0,0 +1,6 @@ +{ + "report": { + "inherits": ["user"], + "allows": ["audits:read-all"] + } +} diff --git a/backend/src/lib/auth.js b/backend/src/lib/auth.js new file mode 100644 index 0000000000000000000000000000000000000000..b9756549feb1b4c0ae3d9bee9004967383a9fc6f --- /dev/null +++ b/backend/src/lib/auth.js @@ -0,0 +1,193 @@ +// Dynamic generation of JWT Secret if not exist (different for each environnment) +var fs = require('fs'); +var env = process.env.NODE_ENV || 'dev'; +var config = require('../config/config.json'); + +if (!config[env].jwtSecret) { + config[env].jwtSecret = require('crypto').randomBytes(32).toString('hex'); + var configString = JSON.stringify(config, null, 4); + fs.writeFileSync(`${__basedir}/config/config.json`, configString); +} +if (!config[env].jwtRefreshSecret) { + config[env].jwtRefreshSecret = require('crypto') + .randomBytes(32) + .toString('hex'); + var configString = JSON.stringify(config, null, 4); + fs.writeFileSync(`${__basedir}/config/config.json`, configString); +} + +var jwtSecret = config[env].jwtSecret; +exports.jwtSecret = jwtSecret; + +var jwtRefreshSecret = config[env].jwtRefreshSecret; +exports.jwtRefreshSecret = jwtRefreshSecret; + +/* ROLES LOGIC + + role_name: { + allows: [], + inherits: [] + } + allows: allowed permissions to access | use * for all + inherits: inherits other users "allows" +*/ + +const builtInRoles = { + user: { + allows: [ + // Audits + 'audits:create', + 'audits:read', + 'audits:update', + 'audits:delete', + // Images + 'images:create', + 'images:read', + // Clients + 'clients:create', + 'clients:read', + 'clients:update', + 'clients:delete', + // Companies + 'companies:create', + 'companies:read', + 'companies:update', + 'companies:delete', + // Languages + 'languages:read', + // Audit Types + 'audit-types:read', + // Vulnerability Types + 'vulnerability-types:read', + // Vulnerability Categories + 'vulnerability-categories:read', + // Sections Data + 'sections:read', + // Templates + 'templates:read', + // Users + 'users:read', + // Roles + 'roles:read', + // Vulnerabilities + 'vulnerabilities:read', + 'vulnerability-updates:create', + // Custom Fields + 'custom-fields:read', + // Settings + 'settings:read-public', + // Classify + 'classify:all', + // Check CWE Update + 'check-update:all', + // Update CWE Model + 'update-model:all', + ], + }, + admin: { + allows: '*', + }, +}; + +try { + var customRoles = require('../config/roles.json'); +} catch (error) { + var customRoles = []; +} +var roles = { ...customRoles, ...builtInRoles }; + +class ACL { + constructor(roles) { + if (typeof roles !== 'object') { + throw new TypeError('Expected an object as input'); + } + this.roles = roles; + } + + isAllowed(role, permission) { + // Check if role exists + if (!this.roles[role] && !this.roles['user']) { + return false; + } + + let $role = this.roles[role] || this.roles['user']; // Default to user role in case of inexistant role + // Check if role is allowed with permission + if ( + $role.allows && + ($role.allows === '*' || $role.allows.indexOf(permission) !== -1) + ) { + return true; + } + + // Check if there is inheritance + if (!$role.inherits || $role.inherits.length < 1) { + return false; + } + + // Recursive check childs until true or false + return $role.inherits.some(role => this.isAllowed(role, permission)); + } + + hasPermission(permission) { + var Response = require('./httpResponse'); + var jwt = require('jsonwebtoken'); + + return (req, res, next) => { + if (!req.cookies['token']) { + Response.Unauthorized(res, 'No token provided'); + return; + } + + var cookie = req.cookies['token'].split(' '); + if (cookie.length !== 2 || cookie[0] !== 'JWT') { + Response.Unauthorized(res, 'Bad token type'); + return; + } + + var token = cookie[1]; + jwt.verify(token, jwtSecret, (err, decoded) => { + if (err) { + if (err.name === 'TokenExpiredError') + Response.Unauthorized(res, 'Expired token'); + else Response.Unauthorized(res, 'Invalid token'); + return; + } + + if ( + permission === 'validtoken' || + this.isAllowed(decoded.role, permission) + ) { + req.decodedToken = decoded; + return next(); + } else { + Response.Forbidden(res, 'Insufficient privileges'); + return; + } + }); + }; + } + + buildRoles(role) { + var currentRole = this.roles[role] || this.roles['user']; // Default to user role in case of inexistant role + + var result = currentRole.allows || []; + + if (currentRole.inherits) { + currentRole.inherits.forEach(element => { + result = [...new Set([...result, ...this.buildRoles(element)])]; + }); + } + + return result; + } + + getRoles(role) { + var result = this.buildRoles(role); + + if (result.includes('*')) return '*'; + + return result; + } +} + +exports.acl = new ACL(roles); diff --git a/backend/src/lib/custom-generator.js b/backend/src/lib/custom-generator.js new file mode 100644 index 0000000000000000000000000000000000000000..d72865243e5d96d3b9e12afb1a2bcb803493d4c2 --- /dev/null +++ b/backend/src/lib/custom-generator.js @@ -0,0 +1,94 @@ +var expressions = require('angular-expressions'); + +// Apply all customs functions +function apply(data) {} +exports.apply = apply; + +// *** Custom modifications of audit data for usage in word template + +// *** Custome Angular expressions filters *** + +var filters = {}; + +// Convert input CVSS criteria into French: {input | criteriaFR} +expressions.filters.criteriaFR = function (input) { + var result = 'Non défini'; + + if (input === 'Network') result = 'Réseau'; + else if (input === 'Adjacent Network') result = 'Réseau Local'; + else if (input === 'Local') result = 'Local'; + else if (input === 'Physical') result = 'Physique'; + else if (input === 'None') result = 'Aucun'; + else if (input === 'Low') result = 'Faible'; + else if (input === 'High') result = 'Haute'; + else if (input === 'Required') result = 'Requis'; + else if (input === 'Unchanged') result = 'Inchangé'; + else if (input === 'Changed') result = 'Changé'; + + return result; +}; + +// Convert input date with parameter s (full,short): {input | convertDate: 's'} +expressions.filters.convertDateFR = function (input, s) { + var date = new Date(input); + if (date !== 'Invalid Date') { + var monthsFull = [ + 'Janvier', + 'Février', + 'Mars', + 'Avril', + 'Mai', + 'Juin', + 'Juillet', + 'Août', + 'Septembre', + 'Octobre', + 'Novembre', + 'Décembre', + ]; + var monthsShort = [ + '01', + '02', + '03', + '04', + '05', + '06', + '07', + '08', + '09', + '10', + '11', + '12', + ]; + var days = [ + 'Dimanche', + 'Lundi', + 'Mardi', + 'Mercredi', + 'Jeudi', + 'Vendredi', + 'Samedi', + ]; + var day = date.getUTCDate(); + var month = date.getUTCMonth(); + var year = date.getUTCFullYear(); + if (s === 'full') { + return ( + days[date.getUTCDay()] + + ' ' + + (day < 10 ? '0' + day : day) + + ' ' + + monthsFull[month] + + ' ' + + year + ); + } + if (s === 'short') { + return ( + (day < 10 ? '0' + day : day) + '/' + monthsShort[month] + '/' + year + ); + } + } +}; + +exports.expressions = expressions; diff --git a/backend/src/lib/cvsscalc31.js b/backend/src/lib/cvsscalc31.js new file mode 100644 index 0000000000000000000000000000000000000000..9d55b51db420873043a9db02da4d76e7b16dece5 --- /dev/null +++ b/backend/src/lib/cvsscalc31.js @@ -0,0 +1,1091 @@ +/* Copyright (c) 2019, FIRST.ORG, INC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the + * following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* This JavaScript contains two main functions. Both take CVSS metric values and calculate CVSS scores for Base, + * Temporal and Environmental metric groups, their associated severity ratings, and an overall Vector String. + * + * Use CVSS31.calculateCVSSFromMetrics if you wish to pass metric values as individual parameters. + * Use CVSS31.calculateCVSSFromVector if you wish to pass metric values as a single Vector String. + * + * Changelog + * + * 2019-06-01 Darius Wiles Updates for CVSS version 3.1: + * + * 1) The CVSS31.roundUp1 function now performs rounding using integer arithmetic to + * eliminate problems caused by tiny errors introduced during JavaScript math + * operations. Thanks to Stanislav Kontar of Red Hat for suggesting and testing + * various implementations. + * + * 2) Environmental formulas changed to prevent the Environmental Score decreasing when + * the value of an Environmental metric is raised. The problem affected a small + * percentage of CVSS v3.0 metrics. The change is to the modifiedImpact + * formula, but only affects scores where the Modified Scope is Changed (or the + * Scope is Changed if Modified Scope is Not Defined). + * + * 3) The JavaScript object containing everything in this file has been renamed from + * "CVSS" to "CVSS31" to allow both objects to be included without causing a + * naming conflict. + * + * 4) Variable names and code order have changed to more closely reflect the formulas + * in the CVSS v3.1 Specification Document. + * + * 5) A successful call to calculateCVSSFromMetrics now returns sub-formula values. + * + * Note that some sets of metrics will produce different scores between CVSS v3.0 and + * v3.1 as a result of changes 1 and 2. See the explanation of changes between these + * two standards in the CVSS v3.1 User Guide for more details. + * + * 2018-02-15 Darius Wiles Added a missing pair of parentheses in the Environmental score, specifically + * in the code setting envScore in the main clause (not the else clause). It was changed + * from "min (...), 10" to "min ((...), 10)". This correction does not alter any final + * Environmental scores. + * + * 2015-08-04 Darius Wiles Added CVSS.generateXMLFromMetrics and CVSS.generateXMLFromVector functions to return + * XML string representations of: a set of metric values; or a Vector String respectively. + * Moved all constants and functions to an object named "CVSS" to + * reduce the chance of conflicts in global variables when this file is combined with + * other JavaScript code. This will break all existing code that uses this file until + * the string "CVSS." is prepended to all references. The "Exploitability" metric has been + * renamed "Exploit Code Maturity" in the specification, so the same change has been made + * in the code in this file. + * + * 2015-04-24 Darius Wiles Environmental formula modified to eliminate undesirable behavior caused by subtle + * differences in rounding between Temporal and Environmental formulas that often + * caused the latter to be 0.1 lower than than the former when all Environmental + * metrics are "Not defined". Also added a RoundUp1 function to simplify formulas. + * + * 2015-04-09 Darius Wiles Added calculateCVSSFromVector function, license information, cleaned up code and improved + * comments. + * + * 2014-12-12 Darius Wiles Initial release for CVSS 3.0 Preview 2. + */ + +// Constants used in the formula. They are not declared as "const" to avoid problems in older browsers. + +var CVSS31 = {}; + +CVSS31.CVSSVersionIdentifier = 'CVSS:3.1'; +CVSS31.exploitabilityCoefficient = 8.22; +CVSS31.scopeCoefficient = 1.08; + +// A regular expression to validate that a CVSS 3.1 vector string is well formed. It checks metrics and metric +// values. It does not check that a metric is specified more than once and it does not check that all base +// metrics are present. These checks need to be performed separately. + +CVSS31.vectorStringRegex_31 = + /^CVSS:3\.[01]\/((AV:[NALP]|AC:[LH]|PR:[UNLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XUNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])\/)*(AV:[NALP]|AC:[LH]|PR:[UNLH]|UI:[NR]|S:[UC]|[CIA]:[NLH]|E:[XUPFH]|RL:[XOTWU]|RC:[XURC]|[CIA]R:[XLMH]|MAV:[XNALP]|MAC:[XLH]|MPR:[XUNLH]|MUI:[XNR]|MS:[XUC]|M[CIA]:[XNLH])$/; + +// Associative arrays mapping each metric value to the constant defined in the CVSS scoring formula in the CVSS v3.1 +// specification. + +CVSS31.Weight = { + AV: { N: 0.85, A: 0.62, L: 0.55, P: 0.2 }, + AC: { H: 0.44, L: 0.77 }, + PR: { + U: { N: 0.85, L: 0.62, H: 0.27 }, // These values are used if Scope is Unchanged + C: { N: 0.85, L: 0.68, H: 0.5 }, + }, // These values are used if Scope is Changed + UI: { N: 0.85, R: 0.62 }, + S: { U: 6.42, C: 7.52 }, // Note: not defined as constants in specification + CIA: { N: 0, L: 0.22, H: 0.56 }, // C, I and A have the same weights + + E: { X: 1, U: 0.91, P: 0.94, F: 0.97, H: 1 }, + RL: { X: 1, O: 0.95, T: 0.96, W: 0.97, U: 1 }, + RC: { X: 1, U: 0.92, R: 0.96, C: 1 }, + + CIAR: { X: 1, L: 0.5, M: 1, H: 1.5 }, // CR, IR and AR have the same weights +}; + +// Severity rating bands, as defined in the CVSS v3.1 specification. + +CVSS31.severityRatings = [ + { name: 'None', bottom: 0.0, top: 0.0 }, + { name: 'Low', bottom: 0.1, top: 3.9 }, + { name: 'Medium', bottom: 4.0, top: 6.9 }, + { name: 'High', bottom: 7.0, top: 8.9 }, + { name: 'Critical', bottom: 9.0, top: 10.0 }, +]; + +/* ** CVSS31.calculateCVSSFromMetrics ** + * + * Takes Base, Temporal and Environmental metric values as individual parameters. Their values are in the short format + * defined in the CVSS v3.1 standard definition of the Vector String. For example, the AttackComplexity parameter + * should be either "H" or "L". + * + * Returns Base, Temporal and Environmental scores, severity ratings, and an overall Vector String. All Base metrics + * are required to generate this output. All Temporal and Environmental metric values are optional. Any that are not + * passed default to "X" ("Not Defined"). + * + * The output is an object which always has a property named "success". + * + * If no errors are encountered, success is Boolean "true", and the following other properties are defined containing + * scores, severities and a vector string: + * baseMetricScore, baseSeverity, + * temporalMetricScore, temporalSeverity, + * environmentalMetricScore, environmentalSeverity, + * vectorString + * + * The following properties are also defined, and contain sub-formula values: + * baseISS, baseImpact, baseExploitability, + * environmentalMISS, environmentalModifiedImpact, environmentalModifiedExploitability + * + * + * If errors are encountered, success is Boolean "false", and the following other properties are defined: + * errorType - a string indicating the error. Either: + * "MissingBaseMetric", if at least one Base metric has not been defined; or + * "UnknownMetricValue", if at least one metric value is invalid. + * errorMetrics - an array of strings representing the metrics at fault. The strings are abbreviated versions of the + * metrics, as defined in the CVSS v3.1 standard definition of the Vector String. + */ +CVSS31.calculateCVSSFromMetrics = function ( + AttackVector, + AttackComplexity, + PrivilegesRequired, + UserInteraction, + Scope, + Confidentiality, + Integrity, + Availability, + ExploitCodeMaturity, + RemediationLevel, + ReportConfidence, + ConfidentialityRequirement, + IntegrityRequirement, + AvailabilityRequirement, + ModifiedAttackVector, + ModifiedAttackComplexity, + ModifiedPrivilegesRequired, + ModifiedUserInteraction, + ModifiedScope, + ModifiedConfidentiality, + ModifiedIntegrity, + ModifiedAvailability, +) { + // If input validation fails, this array is populated with strings indicating which metrics failed validation. + var badMetrics = []; + + // ENSURE ALL BASE METRICS ARE DEFINED + // + // We need values for all Base Score metrics to calculate scores. + // If any Base Score parameters are undefined, create an array of missing metrics and return it with an error. + + if (typeof AttackVector === 'undefined' || AttackVector === '') { + badMetrics.push('AV'); + } + if (typeof AttackComplexity === 'undefined' || AttackComplexity === '') { + badMetrics.push('AC'); + } + if (typeof PrivilegesRequired === 'undefined' || PrivilegesRequired === '') { + badMetrics.push('PR'); + } + if (typeof UserInteraction === 'undefined' || UserInteraction === '') { + badMetrics.push('UI'); + } + if (typeof Scope === 'undefined' || Scope === '') { + badMetrics.push('S'); + } + if (typeof Confidentiality === 'undefined' || Confidentiality === '') { + badMetrics.push('C'); + } + if (typeof Integrity === 'undefined' || Integrity === '') { + badMetrics.push('I'); + } + if (typeof Availability === 'undefined' || Availability === '') { + badMetrics.push('A'); + } + + if (badMetrics.length > 0) { + return { + success: false, + errorType: 'MissingBaseMetric', + errorMetrics: badMetrics, + }; + } + + // STORE THE METRIC VALUES THAT WERE PASSED AS PARAMETERS + // + // Temporal and Environmental metrics are optional, so set them to "X" ("Not Defined") if no value was passed. + + var AV = AttackVector; + var AC = AttackComplexity; + var PR = PrivilegesRequired; + var UI = UserInteraction; + var S = Scope; + var C = Confidentiality; + var I = Integrity; + var A = Availability; + + var E = ExploitCodeMaturity || 'X'; + var RL = RemediationLevel || 'X'; + var RC = ReportConfidence || 'X'; + + var CR = ConfidentialityRequirement || 'X'; + var IR = IntegrityRequirement || 'X'; + var AR = AvailabilityRequirement || 'X'; + var MAV = ModifiedAttackVector || 'X'; + var MAC = ModifiedAttackComplexity || 'X'; + var MPR = ModifiedPrivilegesRequired || 'X'; + var MUI = ModifiedUserInteraction || 'X'; + var MS = ModifiedScope || 'X'; + var MC = ModifiedConfidentiality || 'X'; + var MI = ModifiedIntegrity || 'X'; + var MA = ModifiedAvailability || 'X'; + + // CHECK VALIDITY OF METRIC VALUES + // + // Use the Weight object to ensure that, for every metric, the metric value passed is valid. + // If any invalid values are found, create an array of their metrics and return it with an error. + // + // The Privileges Required (PR) weight depends on Scope, but when checking the validity of PR we must not assume + // that the given value for Scope is valid. We therefore always look at the weights for Unchanged Scope when + // performing this check. The same applies for validation of Modified Privileges Required (MPR). + // + // The Weights object does not contain "X" ("Not Defined") values for Environmental metrics because we replace them + // with their Base metric equivalents later in the function. For example, an MAV of "X" will be replaced with the + // value given for AV. We therefore need to explicitly allow a value of "X" for Environmental metrics. + + if (!CVSS31.Weight.AV.hasOwnProperty(AV)) { + badMetrics.push('AV'); + } + if (!CVSS31.Weight.AC.hasOwnProperty(AC)) { + badMetrics.push('AC'); + } + if (!CVSS31.Weight.PR.U.hasOwnProperty(PR)) { + badMetrics.push('PR'); + } + if (!CVSS31.Weight.UI.hasOwnProperty(UI)) { + badMetrics.push('UI'); + } + if (!CVSS31.Weight.S.hasOwnProperty(S)) { + badMetrics.push('S'); + } + if (!CVSS31.Weight.CIA.hasOwnProperty(C)) { + badMetrics.push('C'); + } + if (!CVSS31.Weight.CIA.hasOwnProperty(I)) { + badMetrics.push('I'); + } + if (!CVSS31.Weight.CIA.hasOwnProperty(A)) { + badMetrics.push('A'); + } + + if (!CVSS31.Weight.E.hasOwnProperty(E)) { + badMetrics.push('E'); + } + if (!CVSS31.Weight.RL.hasOwnProperty(RL)) { + badMetrics.push('RL'); + } + if (!CVSS31.Weight.RC.hasOwnProperty(RC)) { + badMetrics.push('RC'); + } + + if (!(CR === 'X' || CVSS31.Weight.CIAR.hasOwnProperty(CR))) { + badMetrics.push('CR'); + } + if (!(IR === 'X' || CVSS31.Weight.CIAR.hasOwnProperty(IR))) { + badMetrics.push('IR'); + } + if (!(AR === 'X' || CVSS31.Weight.CIAR.hasOwnProperty(AR))) { + badMetrics.push('AR'); + } + if (!(MAV === 'X' || CVSS31.Weight.AV.hasOwnProperty(MAV))) { + badMetrics.push('MAV'); + } + if (!(MAC === 'X' || CVSS31.Weight.AC.hasOwnProperty(MAC))) { + badMetrics.push('MAC'); + } + if (!(MPR === 'X' || CVSS31.Weight.PR.U.hasOwnProperty(MPR))) { + badMetrics.push('MPR'); + } + if (!(MUI === 'X' || CVSS31.Weight.UI.hasOwnProperty(MUI))) { + badMetrics.push('MUI'); + } + if (!(MS === 'X' || CVSS31.Weight.S.hasOwnProperty(MS))) { + badMetrics.push('MS'); + } + if (!(MC === 'X' || CVSS31.Weight.CIA.hasOwnProperty(MC))) { + badMetrics.push('MC'); + } + if (!(MI === 'X' || CVSS31.Weight.CIA.hasOwnProperty(MI))) { + badMetrics.push('MI'); + } + if (!(MA === 'X' || CVSS31.Weight.CIA.hasOwnProperty(MA))) { + badMetrics.push('MA'); + } + + if (badMetrics.length > 0) { + return { + success: false, + errorType: 'UnknownMetricValue', + errorMetrics: badMetrics, + }; + } + + // GATHER WEIGHTS FOR ALL METRICS + + var metricWeightAV = CVSS31.Weight.AV[AV]; + var metricWeightAC = CVSS31.Weight.AC[AC]; + var metricWeightPR = CVSS31.Weight.PR[S][PR]; // PR depends on the value of Scope (S). + var metricWeightUI = CVSS31.Weight.UI[UI]; + var metricWeightS = CVSS31.Weight.S[S]; + var metricWeightC = CVSS31.Weight.CIA[C]; + var metricWeightI = CVSS31.Weight.CIA[I]; + var metricWeightA = CVSS31.Weight.CIA[A]; + + var metricWeightE = CVSS31.Weight.E[E]; + var metricWeightRL = CVSS31.Weight.RL[RL]; + var metricWeightRC = CVSS31.Weight.RC[RC]; + + // For metrics that are modified versions of Base Score metrics, e.g. Modified Attack Vector, use the value of + // the Base Score metric if the modified version value is "X" ("Not Defined"). + var metricWeightCR = CVSS31.Weight.CIAR[CR]; + var metricWeightIR = CVSS31.Weight.CIAR[IR]; + var metricWeightAR = CVSS31.Weight.CIAR[AR]; + var metricWeightMAV = CVSS31.Weight.AV[MAV !== 'X' ? MAV : AV]; + var metricWeightMAC = CVSS31.Weight.AC[MAC !== 'X' ? MAC : AC]; + var metricWeightMPR = + CVSS31.Weight.PR[MS !== 'X' ? MS : S][MPR !== 'X' ? MPR : PR]; // Depends on MS. + var metricWeightMUI = CVSS31.Weight.UI[MUI !== 'X' ? MUI : UI]; + var metricWeightMS = CVSS31.Weight.S[MS !== 'X' ? MS : S]; + var metricWeightMC = CVSS31.Weight.CIA[MC !== 'X' ? MC : C]; + var metricWeightMI = CVSS31.Weight.CIA[MI !== 'X' ? MI : I]; + var metricWeightMA = CVSS31.Weight.CIA[MA !== 'X' ? MA : A]; + + // CALCULATE THE CVSS BASE SCORE + + var iss; /* Impact Sub-Score */ + var impact; + var exploitability; + var baseScore; + + iss = 1 - (1 - metricWeightC) * (1 - metricWeightI) * (1 - metricWeightA); + + if (S === 'U') { + impact = metricWeightS * iss; + } else { + impact = metricWeightS * (iss - 0.029) - 3.25 * Math.pow(iss - 0.02, 15); + } + + exploitability = + CVSS31.exploitabilityCoefficient * + metricWeightAV * + metricWeightAC * + metricWeightPR * + metricWeightUI; + + if (impact <= 0) { + baseScore = 0; + } else { + if (S === 'U') { + baseScore = CVSS31.roundUp1(Math.min(exploitability + impact, 10)); + } else { + baseScore = CVSS31.roundUp1( + Math.min(CVSS31.scopeCoefficient * (exploitability + impact), 10), + ); + } + } + + // CALCULATE THE CVSS TEMPORAL SCORE + + var temporalScore = CVSS31.roundUp1( + baseScore * metricWeightE * metricWeightRL * metricWeightRC, + ); + + // CALCULATE THE CVSS ENVIRONMENTAL SCORE + // + // - modifiedExploitability recalculates the Base Score Exploitability sub-score using any modified values from the + // Environmental metrics group in place of the values specified in the Base Score, if any have been defined. + // - modifiedImpact recalculates the Base Score Impact sub-score using any modified values from the + // Environmental metrics group in place of the values specified in the Base Score, and any additional weightings + // given in the Environmental metrics group. + + var miss; /* Modified Impact Sub-Score */ + var modifiedImpact; + var envScore; + var modifiedExploitability; + + miss = Math.min( + 1 - + (1 - metricWeightMC * metricWeightCR) * + (1 - metricWeightMI * metricWeightIR) * + (1 - metricWeightMA * metricWeightAR), + 0.915, + ); + + if (MS === 'U' || (MS === 'X' && S === 'U')) { + modifiedImpact = metricWeightMS * miss; + } else { + modifiedImpact = + metricWeightMS * (miss - 0.029) - + 3.25 * Math.pow(miss * 0.9731 - 0.02, 13); + } + + modifiedExploitability = + CVSS31.exploitabilityCoefficient * + metricWeightMAV * + metricWeightMAC * + metricWeightMPR * + metricWeightMUI; + + if (modifiedImpact <= 0) { + envScore = 0; + } else if (MS === 'U' || (MS === 'X' && S === 'U')) { + envScore = CVSS31.roundUp1( + CVSS31.roundUp1(Math.min(modifiedImpact + modifiedExploitability, 10)) * + metricWeightE * + metricWeightRL * + metricWeightRC, + ); + } else { + envScore = CVSS31.roundUp1( + CVSS31.roundUp1( + Math.min( + CVSS31.scopeCoefficient * (modifiedImpact + modifiedExploitability), + 10, + ), + ) * + metricWeightE * + metricWeightRL * + metricWeightRC, + ); + } + + // CONSTRUCT THE VECTOR STRING + + var vectorString = + CVSS31.CVSSVersionIdentifier + + '/AV:' + + AV + + '/AC:' + + AC + + '/PR:' + + PR + + '/UI:' + + UI + + '/S:' + + S + + '/C:' + + C + + '/I:' + + I + + '/A:' + + A; + + if (E !== 'X') { + vectorString = vectorString + '/E:' + E; + } + if (RL !== 'X') { + vectorString = vectorString + '/RL:' + RL; + } + if (RC !== 'X') { + vectorString = vectorString + '/RC:' + RC; + } + + if (CR !== 'X') { + vectorString = vectorString + '/CR:' + CR; + } + if (IR !== 'X') { + vectorString = vectorString + '/IR:' + IR; + } + if (AR !== 'X') { + vectorString = vectorString + '/AR:' + AR; + } + if (MAV !== 'X') { + vectorString = vectorString + '/MAV:' + MAV; + } + if (MAC !== 'X') { + vectorString = vectorString + '/MAC:' + MAC; + } + if (MPR !== 'X') { + vectorString = vectorString + '/MPR:' + MPR; + } + if (MUI !== 'X') { + vectorString = vectorString + '/MUI:' + MUI; + } + if (MS !== 'X') { + vectorString = vectorString + '/MS:' + MS; + } + if (MC !== 'X') { + vectorString = vectorString + '/MC:' + MC; + } + if (MI !== 'X') { + vectorString = vectorString + '/MI:' + MI; + } + if (MA !== 'X') { + vectorString = vectorString + '/MA:' + MA; + } + + // Return an object containing the scores for all three metric groups, and an overall vector string. + // Sub-formula values are also included. + + return { + success: true, + + baseMetricScore: baseScore.toFixed(1), + baseSeverity: CVSS31.severityRating(baseScore.toFixed(1)), + baseISS: iss, + baseImpact: impact, + baseExploitability: exploitability, + + temporalMetricScore: temporalScore.toFixed(1), + temporalSeverity: CVSS31.severityRating(temporalScore.toFixed(1)), + + environmentalMetricScore: envScore.toFixed(1), + environmentalSeverity: CVSS31.severityRating(envScore.toFixed(1)), + environmentalMISS: miss, + environmentalModifiedImpact: modifiedImpact, + environmentalModifiedExploitability: modifiedExploitability, + + vectorString: vectorString, + }; +}; + +/* ** CVSS31.calculateCVSSFromVector ** + * + * Takes Base, Temporal and Environmental metric values as a single string in the Vector String format defined + * in the CVSS v3.1 standard definition of the Vector String. + * + * Returns Base, Temporal and Environmental scores, severity ratings, and an overall Vector String. All Base metrics + * are required to generate this output. All Temporal and Environmental metric values are optional. Any that are not + * passed default to "X" ("Not Defined"). + * + * See the comment for the CVSS31.calculateCVSSFromMetrics function for details on the function output. In addition to + * the error conditions listed for that function, this function can also return: + * "MalformedVectorString", if the Vector String passed does not conform to the format in the standard; or + * "MultipleDefinitionsOfMetric", if the Vector String is well formed but defines the same metric (or metrics), + * more than once. + */ +CVSS31.calculateCVSSFromVector = function (vectorString) { + var metricValues = { + AV: undefined, + AC: undefined, + PR: undefined, + UI: undefined, + S: undefined, + C: undefined, + I: undefined, + A: undefined, + E: undefined, + RL: undefined, + RC: undefined, + CR: undefined, + IR: undefined, + AR: undefined, + MAV: undefined, + MAC: undefined, + MPR: undefined, + MUI: undefined, + MS: undefined, + MC: undefined, + MI: undefined, + MA: undefined, + }; + + // If input validation fails, this array is populated with strings indicating which metrics failed validation. + var badMetrics = []; + + if (!CVSS31.vectorStringRegex_31.test(vectorString)) { + return { success: false, errorType: 'MalformedVectorString' }; + } + + var metricNameValue = vectorString + .substring(CVSS31.CVSSVersionIdentifier.length) + .split('/'); + + for (var i in metricNameValue) { + if (metricNameValue.hasOwnProperty(i)) { + var singleMetric = metricNameValue[i].split(':'); + + if (typeof metricValues[singleMetric[0]] === 'undefined') { + metricValues[singleMetric[0]] = singleMetric[1]; + } else { + badMetrics.push(singleMetric[0]); + } + } + } + + if (badMetrics.length > 0) { + return { + success: false, + errorType: 'MultipleDefinitionsOfMetric', + errorMetrics: badMetrics, + }; + } + + return CVSS31.calculateCVSSFromMetrics( + metricValues.AV, + metricValues.AC, + metricValues.PR, + metricValues.UI, + metricValues.S, + metricValues.C, + metricValues.I, + metricValues.A, + metricValues.E, + metricValues.RL, + metricValues.RC, + metricValues.CR, + metricValues.IR, + metricValues.AR, + metricValues.MAV, + metricValues.MAC, + metricValues.MPR, + metricValues.MUI, + metricValues.MS, + metricValues.MC, + metricValues.MI, + metricValues.MA, + ); +}; + +/* ** CVSS31.roundUp1 ** + * + * Rounds up its parameter to 1 decimal place and returns the result. + * + * Standard JavaScript errors thrown when arithmetic operations are performed on non-numbers will be returned if the + * given input is not a number. + * + * Implementation note: Tiny representation errors in floating point numbers makes rounding complex. For example, + * consider calculating Math.ceil((1-0.58)*100) by hand. It can be simplified to Math.ceil(0.42*100), then + * Math.ceil(42), and finally 42. Most JavaScript implementations give 43. The problem is that, on many systems, + * 1-0.58 = 0.42000000000000004, and the tiny error is enough to push ceil up to the next integer. The implementation + * below avoids such problems by performing the rounding using integers. The input is first multiplied by 100,000 + * and rounded to the nearest integer to consider 6 decimal places of accuracy, so 0.000001 results in 0.0, but + * 0.000009 results in 0.1. + * + * A more elegant solution may be possible, but the following gives answers consistent with results from an arbitrary + * precision library. + */ +CVSS31.roundUp1 = function Roundup(input) { + var int_input = Math.round(input * 100000); + + if (int_input % 10000 === 0) { + return int_input / 100000; + } else { + return (Math.floor(int_input / 10000) + 1) / 10; + } +}; + +/* ** CVSS31.severityRating ** + * + * Given a CVSS score, returns the name of the severity rating as defined in the CVSS standard. + * The input needs to be a number between 0.0 to 10.0, to one decimal place of precision. + * + * The following error values may be returned instead of a severity rating name: + * NaN (JavaScript "Not a Number") - if the input is not a number. + * undefined - if the input is a number that is not within the range of any defined severity rating. + */ +CVSS31.severityRating = function (score) { + var severityRatingLength = CVSS31.severityRatings.length; + + var validatedScore = Number(score); + + if (isNaN(validatedScore)) { + return validatedScore; + } + + for (var i = 0; i < severityRatingLength; i++) { + if ( + score >= CVSS31.severityRatings[i].bottom && + score <= CVSS31.severityRatings[i].top + ) { + return CVSS31.severityRatings[i].name; + } + } + + return undefined; +}; + +/////////////////////////////////////////////////////////////////////////// +// DATA AND FUNCTIONS FOR CREATING AN XML REPRESENTATION OF A CVSS SCORE // +/////////////////////////////////////////////////////////////////////////// + +// A mapping between abbreviated metric values and the string used in the XML representation. +// For example, a Remediation Level (RL) abbreviated metric value of "W" maps to "WORKAROUND". +// For brevity, every Base metric shares its definition with its equivalent Environmental metric. This is possible +// because the metric values are same between these groups, except that the latter have an additional metric value +// of "NOT_DEFINED". + +CVSS31.XML_MetricNames = { + E: { + X: 'NOT_DEFINED', + U: 'UNPROVEN', + P: 'PROOF_OF_CONCEPT', + F: 'FUNCTIONAL', + H: 'HIGH', + }, + RL: { + X: 'NOT_DEFINED', + O: 'OFFICIAL_FIX', + T: 'TEMPORARY_FIX', + W: 'WORKAROUND', + U: 'UNAVAILABLE', + }, + RC: { X: 'NOT_DEFINED', U: 'UNKNOWN', R: 'REASONABLE', C: 'CONFIRMED' }, + + CIAR: { X: 'NOT_DEFINED', L: 'LOW', M: 'MEDIUM', H: 'HIGH' }, // CR, IR and AR use the same values + MAV: { + N: 'NETWORK', + A: 'ADJACENT_NETWORK', + L: 'LOCAL', + P: 'PHYSICAL', + X: 'NOT_DEFINED', + }, + MAC: { H: 'HIGH', L: 'LOW', X: 'NOT_DEFINED' }, + MPR: { N: 'NONE', L: 'LOW', H: 'HIGH', X: 'NOT_DEFINED' }, + MUI: { N: 'NONE', R: 'REQUIRED', X: 'NOT_DEFINED' }, + MS: { U: 'UNCHANGED', C: 'CHANGED', X: 'NOT_DEFINED' }, + MCIA: { N: 'NONE', L: 'LOW', H: 'HIGH', X: 'NOT_DEFINED' }, // C, I and A use the same values +}; + +/* ** CVSS31.generateXMLFromMetrics ** + * + * Takes Base, Temporal and Environmental metric values as individual parameters. Their values are in the short format + * defined in the CVSS v3.1 standard definition of the Vector String. For example, the AttackComplexity parameter + * should be either "H" or "L". + * + * Returns a single string containing the metric values in XML form. All Base metrics are required to generate this + * output. All Temporal and Environmental metric values are optional. Any that are not passed will be represented in + * the XML as NOT_DEFINED. The function returns a string for simplicity. It is arguably better to return the XML as + * a DOM object, but at the time of writing this leads to complexity due to older browsers using different JavaScript + * interfaces to do this. Also for simplicity, all Temporal and Environmental metrics are included in the string, + * even though those with a value of "Not Defined" do not need to be included. + * + * The output of this function is an object which always has a property named "success". + * + * If no errors are encountered, success is Boolean "true", and the "xmlString" property contains the XML string + * representation. + * + * If errors are encountered, success is Boolean "false", and other properties are defined as per the + * CVSS31.calculateCVSSFromMetrics function. Refer to the comment for that function for more details. + */ +CVSS31.generateXMLFromMetrics = function ( + AttackVector, + AttackComplexity, + PrivilegesRequired, + UserInteraction, + Scope, + Confidentiality, + Integrity, + Availability, + ExploitCodeMaturity, + RemediationLevel, + ReportConfidence, + ConfidentialityRequirement, + IntegrityRequirement, + AvailabilityRequirement, + ModifiedAttackVector, + ModifiedAttackComplexity, + ModifiedPrivilegesRequired, + ModifiedUserInteraction, + ModifiedScope, + ModifiedConfidentiality, + ModifiedIntegrity, + ModifiedAvailability, +) { + // A string containing the XML we wish to output, with placeholders for the CVSS metrics we will substitute for + // their values, based on the inputs passed to this function. + var xmlTemplate = + '\n' + + '\n' + + '\n' + + ' \n' + + ' __AttackVector__\n' + + ' __AttackComplexity__\n' + + ' __PrivilegesRequired__\n' + + ' __UserInteraction__\n' + + ' __Scope__\n' + + ' __Confidentiality__\n' + + ' __Integrity__\n' + + ' __Availability__\n' + + ' __BaseScore__\n' + + ' __BaseSeverityRating__\n' + + ' \n' + + '\n' + + ' \n' + + ' __ExploitCodeMaturity__\n' + + ' __RemediationLevel__\n' + + ' __ReportConfidence__\n' + + ' __TemporalScore__\n' + + ' __TemporalSeverityRating__\n' + + ' \n' + + '\n' + + ' \n' + + ' __ConfidentialityRequirement__\n' + + ' __IntegrityRequirement__\n' + + ' __AvailabilityRequirement__\n' + + ' __ModifiedAttackVector__\n' + + ' __ModifiedAttackComplexity__\n' + + ' __ModifiedPrivilegesRequired__\n' + + ' __ModifiedUserInteraction__\n' + + ' __ModifiedScope__\n' + + ' __ModifiedConfidentiality__\n' + + ' __ModifiedIntegrity__\n' + + ' __ModifiedAvailability__\n' + + ' __EnvironmentalScore__\n' + + ' __EnvironmentalSeverityRating__\n' + + ' \n' + + '\n' + + '\n'; + + // Call CVSS31.calculateCVSSFromMetrics to validate all the parameters and generate scores and severity ratings. + // If that function returns an error, immediately return it to the caller of this function. + var result = CVSS31.calculateCVSSFromMetrics( + AttackVector, + AttackComplexity, + PrivilegesRequired, + UserInteraction, + Scope, + Confidentiality, + Integrity, + Availability, + ExploitCodeMaturity, + RemediationLevel, + ReportConfidence, + ConfidentialityRequirement, + IntegrityRequirement, + AvailabilityRequirement, + ModifiedAttackVector, + ModifiedAttackComplexity, + ModifiedPrivilegesRequired, + ModifiedUserInteraction, + ModifiedScope, + ModifiedConfidentiality, + ModifiedIntegrity, + ModifiedAvailability, + ); + + if (result.success !== true) { + return result; + } + + var xmlOutput = xmlTemplate; + xmlOutput = xmlOutput.replace( + '__AttackVector__', + CVSS31.XML_MetricNames['MAV'][AttackVector], + ); + xmlOutput = xmlOutput.replace( + '__AttackComplexity__', + CVSS31.XML_MetricNames['MAC'][AttackComplexity], + ); + xmlOutput = xmlOutput.replace( + '__PrivilegesRequired__', + CVSS31.XML_MetricNames['MPR'][PrivilegesRequired], + ); + xmlOutput = xmlOutput.replace( + '__UserInteraction__', + CVSS31.XML_MetricNames['MUI'][UserInteraction], + ); + xmlOutput = xmlOutput.replace( + '__Scope__', + CVSS31.XML_MetricNames['MS'][Scope], + ); + xmlOutput = xmlOutput.replace( + '__Confidentiality__', + CVSS31.XML_MetricNames['MCIA'][Confidentiality], + ); + xmlOutput = xmlOutput.replace( + '__Integrity__', + CVSS31.XML_MetricNames['MCIA'][Integrity], + ); + xmlOutput = xmlOutput.replace( + '__Availability__', + CVSS31.XML_MetricNames['MCIA'][Availability], + ); + xmlOutput = xmlOutput.replace('__BaseScore__', result.baseMetricScore); + xmlOutput = xmlOutput.replace('__BaseSeverityRating__', result.baseSeverity); + + xmlOutput = xmlOutput.replace( + '__ExploitCodeMaturity__', + CVSS31.XML_MetricNames['E'][ExploitCodeMaturity || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__RemediationLevel__', + CVSS31.XML_MetricNames['RL'][RemediationLevel || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__ReportConfidence__', + CVSS31.XML_MetricNames['RC'][ReportConfidence || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__TemporalScore__', + result.temporalMetricScore, + ); + xmlOutput = xmlOutput.replace( + '__TemporalSeverityRating__', + result.temporalSeverity, + ); + + xmlOutput = xmlOutput.replace( + '__ConfidentialityRequirement__', + CVSS31.XML_MetricNames['CIAR'][ConfidentialityRequirement || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__IntegrityRequirement__', + CVSS31.XML_MetricNames['CIAR'][IntegrityRequirement || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__AvailabilityRequirement__', + CVSS31.XML_MetricNames['CIAR'][AvailabilityRequirement || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__ModifiedAttackVector__', + CVSS31.XML_MetricNames['MAV'][ModifiedAttackVector || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__ModifiedAttackComplexity__', + CVSS31.XML_MetricNames['MAC'][ModifiedAttackComplexity || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__ModifiedPrivilegesRequired__', + CVSS31.XML_MetricNames['MPR'][ModifiedPrivilegesRequired || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__ModifiedUserInteraction__', + CVSS31.XML_MetricNames['MUI'][ModifiedUserInteraction || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__ModifiedScope__', + CVSS31.XML_MetricNames['MS'][ModifiedScope || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__ModifiedConfidentiality__', + CVSS31.XML_MetricNames['MCIA'][ModifiedConfidentiality || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__ModifiedIntegrity__', + CVSS31.XML_MetricNames['MCIA'][ModifiedIntegrity || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__ModifiedAvailability__', + CVSS31.XML_MetricNames['MCIA'][ModifiedAvailability || 'X'], + ); + xmlOutput = xmlOutput.replace( + '__EnvironmentalScore__', + result.environmentalMetricScore, + ); + xmlOutput = xmlOutput.replace( + '__EnvironmentalSeverityRating__', + result.environmentalSeverity, + ); + + return { success: true, xmlString: xmlOutput }; +}; + +/* ** CVSS31.generateXMLFromVector ** + * + * Takes Base, Temporal and Environmental metric values as a single string in the Vector String format defined + * in the CVSS v3.1 standard definition of the Vector String. + * + * Returns an XML string representation of this input. See the comment for CVSS31.generateXMLFromMetrics for more + * detail on inputs, return values and errors. In addition to the error conditions listed for that function, this + * function can also return: + * "MalformedVectorString", if the Vector String passed is does not conform to the format in the standard; or + * "MultipleDefinitionsOfMetric", if the Vector String is well formed but defines the same metric (or metrics), + * more than once. + */ +CVSS31.generateXMLFromVector = function (vectorString) { + var metricValues = { + AV: undefined, + AC: undefined, + PR: undefined, + UI: undefined, + S: undefined, + C: undefined, + I: undefined, + A: undefined, + E: undefined, + RL: undefined, + RC: undefined, + CR: undefined, + IR: undefined, + AR: undefined, + MAV: undefined, + MAC: undefined, + MPR: undefined, + MUI: undefined, + MS: undefined, + MC: undefined, + MI: undefined, + MA: undefined, + }; + + // If input validation fails, this array is populated with strings indicating which metrics failed validation. + var badMetrics = []; + + if (!CVSS31.vectorStringRegex_31.test(vectorString)) { + return { success: false, errorType: 'MalformedVectorString' }; + } + + var metricNameValue = vectorString + .substring(CVSS31.CVSSVersionIdentifier.length) + .split('/'); + + for (var i in metricNameValue) { + if (metricNameValue.hasOwnProperty(i)) { + var singleMetric = metricNameValue[i].split(':'); + + if (typeof metricValues[singleMetric[0]] === 'undefined') { + metricValues[singleMetric[0]] = singleMetric[1]; + } else { + badMetrics.push(singleMetric[0]); + } + } + } + + if (badMetrics.length > 0) { + return { + success: false, + errorType: 'MultipleDefinitionsOfMetric', + errorMetrics: badMetrics, + }; + } + + return CVSS31.generateXMLFromMetrics( + metricValues.AV, + metricValues.AC, + metricValues.PR, + metricValues.UI, + metricValues.S, + metricValues.C, + metricValues.I, + metricValues.A, + metricValues.E, + metricValues.RL, + metricValues.RC, + metricValues.CR, + metricValues.IR, + metricValues.AR, + metricValues.MAV, + metricValues.MAC, + metricValues.MPR, + metricValues.MUI, + metricValues.MS, + metricValues.MC, + metricValues.MI, + metricValues.MA, + ); +}; + +module.exports = CVSS31; diff --git a/backend/src/lib/html2ooxml.js b/backend/src/lib/html2ooxml.js new file mode 100644 index 0000000000000000000000000000000000000000..b1115ab6c52360c6078e92269b92022bfd8ece47 --- /dev/null +++ b/backend/src/lib/html2ooxml.js @@ -0,0 +1,204 @@ +var docx = require('docx'); +var xml = require('xml'); +var htmlparser = require('htmlparser2'); + +function html2ooxml(html, style = '') { + if (html === '') return html; + if (!html.match(/^<.+>/)) html = `

${html}

`; + var doc = new docx.Document({ sections: [] }); + var paragraphs = []; + var cParagraph = null; + var cRunProperties = {}; + var cParagraphProperties = {}; + var list_state = []; + var inCodeBlock = false; + var parser = new htmlparser.Parser( + { + onopentag(tag, attribs) { + if (tag === 'h1') { + cParagraph = new docx.Paragraph({ heading: 'Heading1' }); + } else if (tag === 'h2') { + cParagraph = new docx.Paragraph({ heading: 'Heading2' }); + } else if (tag === 'h3') { + cParagraph = new docx.Paragraph({ heading: 'Heading3' }); + } else if (tag === 'h4') { + cParagraph = new docx.Paragraph({ heading: 'Heading4' }); + } else if (tag === 'h5') { + cParagraph = new docx.Paragraph({ heading: 'Heading5' }); + } else if (tag === 'h6') { + cParagraph = new docx.Paragraph({ heading: 'Heading6' }); + } else if (tag === 'div' || tag === 'p') { + if (style && typeof style === 'string') + cParagraphProperties.style = style; + cParagraph = new docx.Paragraph(cParagraphProperties); + } else if (tag === 'pre') { + inCodeBlock = true; + cParagraph = new docx.Paragraph({ style: 'Code' }); + } else if (tag === 'b' || tag === 'strong') { + cRunProperties.bold = true; + } else if (tag === 'i' || tag === 'em') { + cRunProperties.italics = true; + } else if (tag === 'u') { + cRunProperties.underline = {}; + } else if (tag === 'strike' || tag === 's') { + cRunProperties.strike = true; + } else if (tag === 'mark') { + var bgColor = attribs['data-color'] || '#ffff25'; + cRunProperties.highlight = getHighlightColor(bgColor); + + // Use text color if set (to handle white or black text depending on background color) + var color = attribs.style.match(/.+color:.(.+)/); + if (color && color[1]) cRunProperties.color = getTextColor(color[1]); + } else if (tag === 'br') { + if (inCodeBlock) { + paragraphs.push(cParagraph); + cParagraph = new docx.Paragraph({ style: 'Code' }); + } else cParagraph.addChildElement(new docx.Run({ break: 1 })); + } else if (tag === 'ul') { + list_state.push('bullet'); + } else if (tag === 'ol') { + list_state.push('number'); + } else if (tag === 'li') { + var level = list_state.length - 1; + if (level >= 0 && list_state[level] === 'bullet') + cParagraphProperties.bullet = { level: level }; + else if (level >= 0 && list_state[level] === 'number') + cParagraphProperties.numbering = { reference: 2, level: level }; + else cParagraphProperties.bullet = { level: 0 }; + } else if (tag === 'code') { + cRunProperties.style = 'CodeChar'; + } else if (tag === 'legend' && attribs && attribs.alt !== 'undefined') { + var label = attribs.label || 'Figure'; + cParagraph = new docx.Paragraph({ + style: 'Caption', + alignment: docx.AlignmentType.CENTER, + }); + cParagraph.addChildElement(new docx.TextRun(`${label} `)); + cParagraph.addChildElement(new docx.SimpleField(`SEQ ${label}`, '1')); + cParagraph.addChildElement(new docx.TextRun(` - ${attribs.alt}`)); + } + }, + + ontext(text) { + if (text && cParagraph) { + cRunProperties.text = text; + cParagraph.addChildElement(new docx.TextRun(cRunProperties)); + } + }, + + onclosetag(tag) { + if ( + [ + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'div', + 'p', + 'pre', + 'img', + 'legend', + ].includes(tag) + ) { + paragraphs.push(cParagraph); + cParagraph = null; + cParagraphProperties = {}; + if (tag === 'pre') inCodeBlock = false; + } else if (tag === 'b' || tag === 'strong') { + delete cRunProperties.bold; + } else if (tag === 'i' || tag === 'em') { + delete cRunProperties.italics; + } else if (tag === 'u') { + delete cRunProperties.underline; + } else if (tag === 'strike' || tag === 's') { + delete cRunProperties.strike; + } else if (tag === 'mark') { + delete cRunProperties.highlight; + delete cRunProperties.color; + } else if (tag === 'ul' || tag === 'ol') { + list_state.pop(); + if (list_state.length === 0) cParagraphProperties = {}; + } else if (tag === 'code') { + delete cRunProperties.style; + } + }, + + onend() { + doc.addSection({ + children: paragraphs, + }); + }, + }, + { decodeEntities: true }, + ); + + // For multiline code blocks + html = html.replace(/\n/g, '
'); + parser.write(html); + parser.end(); + + var prepXml = doc.documentWrapper.document.body.prepForXml({}); + var filteredXml = prepXml['w:body'].filter(e => { + return Object.keys(e)[0] === 'w:p'; + }); + var dataXml = xml(filteredXml); + dataXml = dataXml.replace(/w:numId w:val="{2-0}"/g, 'w:numId w:val="2"'); // Replace numbering to have correct value + + return dataXml; +} +module.exports = html2ooxml; + +function getHighlightColor(hexColor) { + // + // + // + // + // + // + // + + // + // + // + // + // + + // + // + // + // + // + + // + // + // + // + + var colors = { + '#ffff25': 'yellow', + '#00ff41': 'green', + '#00ffff': 'cyan', + '#ff00f9': 'magenta', + '#0005fd': 'blue', + '#ff0000': 'red', + '#000177': 'darkBlue', + '#00807a': 'darkCyan', + '#008021': 'darkGreen', + '#8e0075': 'darkMagenta', + '#8f0000': 'darkRed', + '#817d0c': 'darkYellow', + '#807d78': 'darkGray', + '#c4c1bb': 'lightGray', + '#000000': 'black', + }; + return colors[hexColor] || 'yellow'; +} + +function getTextColor(color) { + var regex = /^#[0-9a-fA-F]{6}$/; + if (regex.test(color)) return color.substring(1, 7); + + return '000000'; +} diff --git a/backend/src/lib/httpResponse.js b/backend/src/lib/httpResponse.js new file mode 100644 index 0000000000000000000000000000000000000000..57893fa8499d982c4eaf7e150c0a51751dcfbd86 --- /dev/null +++ b/backend/src/lib/httpResponse.js @@ -0,0 +1,82 @@ +function Custom(res, status, code, message) { + res.status(code).json({ status: status, datas: message }); +} +exports.Custom = Custom; + +/* + *** Codes 2xx *** + */ + +function Ok(res, data) { + res.status(200).json({ status: 'success', datas: data }); +} +exports.Ok = Ok; + +function Created(res, data) { + res.status(201).json({ status: 'success', datas: data }); +} +exports.Created = Created; + +function NoContent(res, data) { + res.status(204).json({ status: 'success', datas: data }); +} +exports.NoContent = NoContent; + +function SendFile(res, filename, file) { + res.set({ 'Content-Disposition': `attachment; filename="${filename}"` }); + res.status(200).send(file); +} +exports.SendFile = SendFile; + +function SendImage(res, image) { + res.set({ 'Content-Type': 'image/png', 'Content-Length': image.length }); + res.status(200).send(image); +} +exports.SendImage = SendImage; + +/* + *** Codes 4xx *** + */ + +function BadRequest(res, error) { + res.status(400).json({ status: 'error', datas: error }); +} +exports.BadRequest = BadRequest; + +function NotFound(res, error) { + res.status(404).json({ status: 'error', datas: error }); +} +exports.NotFound = NotFound; + +function BadParameters(res, error) { + res.status(422).json({ status: 'error', datas: error }); +} +exports.BadParameters = BadParameters; + +function Unauthorized(res, error) { + res.status(401).json({ status: 'error', datas: error }); +} +exports.Unauthorized = Unauthorized; + +function Forbidden(res, error) { + res.status(403).json({ status: 'error', datas: error }); +} +exports.Forbidden = Forbidden; + +/* + *** Codes 5xx *** + */ + +function Internal(res, error) { + if (error.fn) var fn = exports[error.fn]; + if (typeof fn === 'function') fn(res, error.message); + else if (error.errmsg) { + res.status(500).json({ status: 'error', datas: error.errmsg }); + } else if (error.message) + res.status(500).json({ status: 'error', datas: error.message }); + else { + console.log(error); + res.status(500).json({ status: 'error', datas: 'Internal Error' }); + } +} +exports.Internal = Internal; diff --git a/backend/src/lib/passwordpolicy.js b/backend/src/lib/passwordpolicy.js new file mode 100644 index 0000000000000000000000000000000000000000..7bebdb9cfd71c2c87d980eedc49d6060916efe38 --- /dev/null +++ b/backend/src/lib/passwordpolicy.js @@ -0,0 +1,7 @@ +// Check if user's password match password policy +function strongPassword(password) { + var regExp = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}/; + return regExp.test(password); +} + +exports.strongPassword = strongPassword; diff --git a/backend/src/lib/report-filters.js b/backend/src/lib/report-filters.js new file mode 100644 index 0000000000000000000000000000000000000000..2ea7ac52a9af2a11b0d1a7282c871ae06622e414 --- /dev/null +++ b/backend/src/lib/report-filters.js @@ -0,0 +1,423 @@ +var expressions = require('angular-expressions'); +var html2ooxml = require('./html2ooxml'); +var translate = require('../translate'); +var _ = require('lodash'); + +// *** Angular parser filters *** + +// Creates a text block or simple location bookmark: +// - Text block: {@name | bookmarkCreate: identifier | p} +// - Location: {@identifier | bookmarkCreate | p} +// Identifiers are sanitized as follow: +// - Invalid characters replaced by underscores. +// - Identifiers longer than 40 chars are truncated (MS-Word limitation). +expressions.filters.bookmarkCreate = function (input, refid = null) { + let rand_id = Math.floor(Math.random() * 1000000 + 1000); + let parsed_id = (refid ? refid : input) + .replace(/[^a-zA-Z0-9_]/g, '_') + .substring(0, 40); + + // Accept both text and OO-XML as input. + if (input.indexOf(''; + } + + return ( + '' + + (refid ? input : '') + + '' + ); +}; + +// Creates a hyperlink to a text block or location bookmark: +// {@input | bookmarkLink: identifier | p} +// Identifiers are sanitized as follow: +// - Invalid characters replaced by underscores. +// - Identifiers longer than 40 chars are truncated (MS-Word limitation). +expressions.filters.bookmarkLink = function (input, identifier) { + identifier = identifier.replace(/[^a-zA-Z0-9_]/g, '_').substring(0, 40); + return ( + '' + + '' + + '' + + input + + '' + + '' + ); +}; + +// Creates a clickable dynamic field referencing a text block bookmark: +// {@identifier | bookmarkRef | p} +// Identifiers are sanitized as follow: +// - Invalid characters replaced by underscores. +// - Identifiers longer than 40 chars are truncated (MS-Word limitation). +expressions.filters.bookmarkRef = function (input) { + return ( + '' + + ' REF ' + + input.replace(/[^a-zA-Z0-9_]/g, '_').substring(0, 40) + + ' \\h ' + + '' + + input + + '' + ); +}; + +// Capitalizes input first letter: {input | capfirst} +expressions.filters.capfirst = function (input) { + if (!input || input == 'undefined') return input; + return input.replace(/^\w/, c => c.toUpperCase()); +}; + +// Convert input date with parameter s (full,short): {input | convertDate: 's'} +expressions.filters.convertDate = function (input, s) { + var date = new Date(input); + if (date != 'Invalid Date') { + var monthsFull = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', + ]; + var monthsShort = [ + '01', + '02', + '03', + '04', + '05', + '06', + '07', + '08', + '09', + '10', + '11', + '12', + ]; + var days = [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + ]; + var day = date.getUTCDate(); + var month = date.getUTCMonth(); + var year = date.getUTCFullYear(); + if (s === 'full') { + return ( + days[date.getUTCDay()] + + ', ' + + monthsFull[month] + + ' ' + + (day < 10 ? '0' + day : day) + + ', ' + + year + ); + } + if (s === 'short') { + return ( + monthsShort[month] + '/' + (day < 10 ? '0' + day : day) + '/' + year + ); + } + } +}; + +// Convert input date with parameter s (full,short): {input | convertDateLocale: 'locale':'style'} +expressions.filters.convertDateLocale = function (input, locale, style) { + var date = new Date(input); + if (date != 'Invalid Date') { + var options = { year: 'numeric', month: '2-digit', day: '2-digit' }; + + if (style === 'full') + options = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + }; + + return date.toLocaleDateString(locale, options); + } +}; + +// Convert identifier prefix to a user defined prefix: {identifier | changeID: 'PRJ-'} +expressions.filters.changeID = function (input, prefix) { + return input.replace('IDX-', prefix); +}; + +// Default value: returns input if it is truthy, otherwise its parameter. +// Example producing a comma-separated list of affected systems, falling-back on the whole audit scope: {affected | lines | d: (scope | select: 'name') | join: ', '} +expressions.filters.d = function (input, s) { + return input && input != 'undefined' ? input : s; +}; + +// Display "From ... to ..." dates nicely, removing redundant information when the start and end date occur during the same month or year: {date_start | fromTo: date_end:'fr' | capfirst} +// To internationalize or customize the resulting string, associate the desired output to the strings "from {0} to {1}" and "on {0}" in your Pwndoc translate file. +expressions.filters.fromTo = function (start, end, locale) { + const start_date = new Date(start); + const end_date = new Date(end); + let options = {}, + start_str = '', + end_str = ''; + let str = 'from {0} to {1}'; + + if (start_date == 'Invalid Date' || end_date == 'Invalid Date') return start; + + options = { day: '2-digit', month: '2-digit', year: 'numeric' }; + end_str = end_date.toLocaleDateString(locale, options); + + if (start_date.getYear() != end_date.getYear()) { + options = { day: '2-digit', month: '2-digit', year: 'numeric' }; + start_str = start_date.toLocaleDateString(locale, options); + } else if (start_date.getMonth() != end_date.getMonth()) { + options = { day: '2-digit', month: '2-digit' }; + start_str = start_date.toLocaleDateString(locale, options); + } else if (start_date.getDay() != end_date.getDay()) { + options = { day: '2-digit' }; + start_str = start_date.toLocaleDateString(locale, options); + } else { + start_str = end_str; + str = 'on {0}'; + } + + return translate.translate(str).format(start_str, end_str); +}; + +// Group input elements by an attribute: {#findings | groupBy: 'severity'}{title}{/findings | groupBy: 'severity'} +// Source: https://stackoverflow.com/a/34890276 +expressions.filters.groupBy = function (input, key) { + return expressions.filters.loopObject( + input.reduce(function (rv, x) { + (rv[x[key]] = rv[x[key]] || []).push(x); + return rv; + }, {}), + ); +}; + +// Returns the initials from an input string (typically a firstname): {creator.firstname | initials} +expressions.filters.initials = function (input) { + if (!input || input == 'undefined') return input; + return input.replace(/(\w)\w+/gi, '$1.'); +}; + +// Returns a string which is a concatenation of input elements using an optional separator string: {references | join: ', '} +// Can also be used to build raw OOXML strings. +expressions.filters.join = function (input, sep = '') { + if (!input || input == 'undefined') return input; + return input.join(sep); +}; + +// Returns the length (ie. number of items for an array) of input: {input | length} +// Can be used as a conditional to check the emptiness of a list: {#input | length}Not empty{/input | length} +expressions.filters.length = function (input) { + return input.length; +}; + +// Takes a multilines input strings (either raw or simple HTML paragraphs) and returns each line as an ordered list: {input | lines} +expressions.filters.lines = function (input) { + if (!input || input == 'undefined') return input; + if (input.indexOf('

') == 0) { + return input.substring(3, input.length - 4).split('

'); + } else { + return input.split('\n'); + } +}; + +// Creates a hyperlink: {@input | linkTo: 'https://example.com' | p} +expressions.filters.linkTo = function (input, url) { + return ( + '' + + ' HYPERLINK "' + + url + + '" ' + + '' + + '' + + '' + + input + + '' + + '' + ); +}; + +// Loop over the input object, providing acccess to its keys and values: {#findings | loopObject}{key}{value.title}{/findings | loopObject} +// Source: https://stackoverflow.com/a/60887987 +expressions.filters.loopObject = function (input) { + return Object.keys(input).map(function (key) { + return { key, value: input[key] }; + }); +}; + +// Lowercases input: {input | lower} +expressions.filters.lower = function (input) { + if (!input || input == 'undefined') return input; + return input.toLowerCase(); +}; + +// Creates a clickable "mailto:" link, assumes that input is an email address if +// no other address has been provided as parameter: +// {@lastname | mailto: email | p} +expressions.filters.mailto = function (input, address = null) { + return expressions.filters.linkTo( + input, + 'mailto:' + (address ? address : input), + ); +}; + +// Applies a filter on a sequence of objects: {scope | select: 'name' | map: 'lower' | join: ', '} +expressions.filters.map = function (input, filter) { + let args = Array.prototype.slice.call(arguments, 2); + return input.map(x => expressions.filters[filter](x, ...args)); +}; + +// Replace newlines in office XML format: {@input | NewLines} +expressions.filters.NewLines = function (input) { + var pre = ''; + var post = ''; + var lineBreak = ''; + var result = ''; + + if (!input) return pre + post; + + input = utils.escapeXMLEntities(input); + var inputArray = input.split(/\n\n+/g); + inputArray.forEach(p => { + result += `${pre}${p.replace(/\n/g, lineBreak)}${post}`; + }); + // input = input.replace(/\n/g, lineBreak); + // return pre + input + post; + return result; +}; + +// Embeds input within OOXML paragraph tags, applying an optional style name to it: {@input | p: 'Some style'} +expressions.filters.p = function (input, style = null) { + let result = ''; + + if (style !== null) { + let style_parsed = style.replaceAll(' ', ''); + result += ''; + } + result += input + ''; + + return result; +}; + +// Reverses the input array: {input | reverse} +expressions.filters.reverse = function (input) { + return input.reverse(); +}; + +// Add proper XML tags to embed raw string inside a docxtemplater raw expression: {@('Vulnerability: ' | s) + title | bookmarkCreate: identifier | p} +expressions.filters.s = function (input) { + return '' + input + ''; +}; + +// Looks up an attribute from a sequence of objects, doted notation is supported: {findings | select: 'cvss.environmentalSeverity'} +expressions.filters.select = function (input, attr) { + return input.map(function (item) { + return _.get(item, attr); + }); +}; + +// Sorts the input array according an optional given attribute, dotted notation is supported: {#findings | sort 'cvss.environmentalSeverity'}{name}{/findings | sort 'cvss.environmentalSeverity'} +expressions.filters.sort = function (input, key = null) { + if (key === null) { + return input.sort(); + } else { + return input.sort(function (a, b) { + return _.get(a, key) < _.get(b, key); + }); + } +}; + +// Sort array by supplied field: {#findings | sortArrayByField: 'identifier':1}{/} +// order: 1 = ascending, -1 = descending +expressions.filters.sortArrayByField = function (input, field, order) { + //invalid order sort ascending + if (order != 1 && order != -1) order = 1; + + const sorted = input.sort((a, b) => { + //multiply by order so that if is descending (-1) will reverse the values + return ( + _.get(a, field).localeCompare(_.get(b, field), undefined, { + numeric: true, + }) * order + ); + }); + return sorted; +}; + +// Capitalizes input first letter of each word, can be associated to 'lower' to normalize case: {creator.lastname | lower | title} +expressions.filters.title = function (input) { + if (!input || input == 'undefined') return input; + return input.replace(/\w\S*/g, w => w.replace(/^\w/, c => c.toUpperCase())); +}; + +// Returns the JSON representation of the input value, useful to dump variables content while debugging a template: {input | toJSON} +expressions.filters.toJSON = function (input) { + return JSON.stringify(input); +}; + +// Upercases input: {input | upper} +expressions.filters.upper = function (input) { + if (!input || input == 'undefined') return input; + return input.toUpperCase(); +}; + +// Filters input elements matching a free-form Angular statements: {#findings | where: 'cvss.severity == "Critical"'}{title}{/findings | where: 'cvss.severity == "Critical"'} +// Source: https://docxtemplater.com/docs/angular-parse/#data-filtering +expressions.filters.where = function (input, query) { + return input.filter(function (item) { + return expressions.compile(query)(item); + }); +}; + +// Convert HTML data to Open Office XML format: {@input | convertHTML: 'customStyle'} +expressions.filters.convertHTML = function (input, style) { + if (typeof input === 'undefined') var result = html2ooxml(''); + else var result = html2ooxml(input.replace(/(

<\/p>)+$/, ''), style); + return result; +}; + +// Count vulnerability by severity +// Example: {findings | count: 'Critical'} +expressions.filters.count = function (input, severity) { + if (!input) return input; + var count = 0; + + for (var i = 0; i < input.length; i++) { + if (input[i].cvss.baseSeverity === severity) { + count += 1; + } + } + + return count; +}; + +// Translate using locale from 'translate' folder +// Example: {input | translate: 'fr'} +expressions.filters.translate = function (input, locale) { + translate.setLocale(locale); + if (!input) return input; + return translate.translate(input, locale); +}; + +module.exports = expressions; diff --git a/backend/src/lib/report-generator.js b/backend/src/lib/report-generator.js new file mode 100644 index 0000000000000000000000000000000000000000..2cc0813311c5de3b78befa72435cf2928cc7b37c --- /dev/null +++ b/backend/src/lib/report-generator.js @@ -0,0 +1,707 @@ +var fs = require('fs'); +var Docxtemplater = require('docxtemplater'); +var PizZip = require('pizzip'); +var expressions = require('./report-filters'); +var ImageModule = require('docxtemplater-image-module-pwndoc'); +var sizeOf = require('image-size'); +var customGenerator = require('./custom-generator'); +var utils = require('./utils'); +var _ = require('lodash'); +var Image = require('mongoose').model('Image'); +const libre = require('libreoffice-convert'); +const { parseAsync } = require('json2csv'); +var Settings = require('mongoose').model('Settings'); +var CVSS31 = require('./cvsscalc31.js'); +var translate = require('../translate'); +var $t; +const muhammara = require('muhammara'); +const path = require('path'); +const os = require('os'); +const { v4: uuidv4 } = require('uuid'); + +// Generate document with docxtemplater +async function generateDoc(audit) { + var templatePath = `${__basedir}/../report-templates/${audit.template.name}.${audit.template.ext || 'docx'}`; + var content = fs.readFileSync(templatePath, 'binary'); + + var zip = new PizZip(content); + + translate.setLocale(audit.language); + $t = translate.translate; + + var settings = await Settings.getAll(); + var preppedAudit = await prepAuditData(audit, settings); + + var opts = {}; + // opts.centered = true; + opts.getImage = function (tagValue, tagName) { + if (tagValue !== 'undefined') { + tagValue = tagValue.split(',')[1]; + return Buffer.from(tagValue, 'base64'); + } + // return fs.readFileSync(tagValue, {encoding: 'base64'}); + }; + opts.getSize = function (img, tagValue, tagName) { + if (img) { + var sizeObj = sizeOf(img); + var width = sizeObj.width; + var height = sizeObj.height; + if (tagName === 'company.logo_small') { + var divider = sizeObj.height / 37; + height = 37; + width = Math.floor(sizeObj.width / divider); + } else if (tagName === 'company.logo') { + var divider = sizeObj.height / 250; + height = 250; + width = Math.floor(sizeObj.width / divider); + if (width > 400) { + divider = sizeObj.width / 400; + height = Math.floor(sizeObj.height / divider); + width = 400; + } + } else if (sizeObj.width > 600) { + var divider = sizeObj.width / 600; + width = 600; + height = Math.floor(sizeObj.height / divider); + } + return [width, height]; + } + return [0, 0]; + }; + + if ( + settings.report.private.imageBorder && + settings.report.private.imageBorderColor + ) + opts.border = settings.report.private.imageBorderColor.replace('#', ''); + + try { + var imageModule = new ImageModule(opts); + } catch (err) { + console.log(err); + } + var doc = new Docxtemplater() + .attachModule(imageModule) + .loadZip(zip) + .setOptions({ parser: parser, paragraphLoop: true }); + customGenerator.apply(preppedAudit); + doc.setData(preppedAudit); + try { + doc.render(); + } catch (error) { + if (error.properties.id === 'multi_error') { + error.properties.errors.forEach(function (err) { + console.log(err); + }); + } else console.log(error); + if (error.properties && error.properties.errors instanceof Array) { + const errorMessages = error.properties.errors + .map(function (error) { + return `Explanation: ${error.properties.explanation}\nScope: ${JSON.stringify(error.properties.scope).substring(0, 142)}...`; + }) + .join('\n\n'); + // errorMessages is a humanly readable message looking like this : + // 'The tag beginning with "foobar" is unopened' + throw `Template Error:\n${errorMessages}`; + } else { + throw error; + } + } + var buf = doc.getZip().generate({ type: 'nodebuffer' }); + + return buf; +} +exports.generateDoc = generateDoc; + +// Generates a PDF from a docx using libreoffice-convert +// libreoffice-convert leverages libreoffice to convert office documents to different formats +// https://www.npmjs.com/package/libreoffice-convert +async function generatePdf(audit) { + var docxReport = await generateDoc(audit); + return new Promise((resolve, reject) => + libre.convert(docxReport, '.pdf', undefined, (err, pdf) => { + if (err) console.log(err); + resolve(pdf); + }), + ); +} +exports.generatePdf = generatePdf; + +// Generates a encrypted PDF using libreoffice-convert +// and muhammara to encrypt it with a given password. +// https://www.npmjs.com/package/muhammara + +async function generateEncryptedPdf(audit, password) { + // Genera el archivo DOCX + var docxReport = await generateDoc(audit); + + return new Promise((resolve, reject) => { + libre.convert(docxReport, '.pdf', undefined, (err, pdf) => { + if (err) { + console.log(err); + return reject(err); + } + + const tempPdfPath = path.join( + os.tmpdir(), + `documento_sin_contraseña_${uuidv4()}.pdf`, + ); + fs.writeFileSync(tempPdfPath, pdf); + + const protectedPdfPath = path.join( + os.tmpdir(), + `documento_protegido_${uuidv4()}.pdf`, + ); + + try { + const Recipe = muhammara.Recipe; + const pdfDoc = new Recipe(tempPdfPath, protectedPdfPath); + + pdfDoc + .encrypt({ + userPassword: password, + ownerPassword: password, + userProtectionFlag: 4, + }) + .endPDF(); + + const protectedPdf = fs.readFileSync(protectedPdfPath); + + fs.unlinkSync(tempPdfPath); + fs.unlinkSync(protectedPdfPath); + + resolve(protectedPdf); + } catch (error) { + console.error('Error protecting PDF:', error); + reject(error); + } + }); + }); +} +exports.generateEncryptedPdf = generateEncryptedPdf; + +// Generates a csv from the json data +// Leverages json2csv +// https://www.npmjs.com/package/json2csv +async function generateCsv(audit) { + return parseAsync(audit._doc); +} +exports.generateCsv = generateCsv; + +// Filters helper: handles the use of preformated easilly translatable strings. +// Source: https://www.tutorialstonight.com/javascript-string-format.php +String.prototype.format = function () { + let args = arguments; + return this.replace(/{([0-9]+)}/g, function (match, index) { + return typeof args[index] == 'undefined' ? match : args[index]; + }); +}; + +// Compile all angular expressions +var angularParser = function (tag) { + expressions = { ...expressions, ...customGenerator.expressions }; + if (tag === '.') { + return { + get: function (s) { + return s; + }, + }; + } + const expr = expressions.compile( + tag.replace(/(’|‘)/g, "'").replace(/(“|”)/g, '"'), + ); + return { + get: function (scope, context) { + let obj = {}; + const scopeList = context.scopeList; + const num = context.num; + for (let i = 0, len = num + 1; i < len; i++) { + obj = _.merge(obj, scopeList[i]); + } + return expr(scope, obj); + }, + }; +}; + +function parser(tag) { + // We write an exception to handle the tag "$pageBreakExceptLast" + if (tag === '$pageBreakExceptLast') { + return { + get(scope, context) { + const totalLength = + context.scopePathLength[context.scopePathLength.length - 1]; + const index = context.scopePathItem[context.scopePathItem.length - 1]; + const isLast = index === totalLength - 1; + if (!isLast) { + return ''; + } else { + return ''; + } + }, + }; + } + // We use the angularParser as the default fallback + // If you don't wish to use the angularParser, + // you can use the default parser as documented here: + // https://docxtemplater.readthedocs.io/en/latest/configuration.html#default-parser + return angularParser(tag); +} +function cvssStrToObject(cvss) { + var initialState = 'Not Defined'; + var res = { + AV: initialState, + AC: initialState, + PR: initialState, + UI: initialState, + S: initialState, + C: initialState, + I: initialState, + A: initialState, + E: initialState, + RL: initialState, + RC: initialState, + CR: initialState, + IR: initialState, + AR: initialState, + MAV: initialState, + MAC: initialState, + MPR: initialState, + MUI: initialState, + MS: initialState, + MC: initialState, + MI: initialState, + MA: initialState, + }; + if (cvss) { + var temp = cvss.split('/'); + for (var i = 0; i < temp.length; i++) { + var elt = temp[i].split(':'); + switch (elt[0]) { + case 'AV': + if (elt[1] === 'N') res.AV = 'Network'; + else if (elt[1] === 'A') res.AV = 'Adjacent Network'; + else if (elt[1] === 'L') res.AV = 'Local'; + else if (elt[1] === 'P') res.AV = 'Physical'; + res.AV = $t(res.AV); + break; + case 'AC': + if (elt[1] === 'L') res.AC = 'Low'; + else if (elt[1] === 'H') res.AC = 'High'; + res.AC = $t(res.AC); + break; + case 'PR': + if (elt[1] === 'N') res.PR = 'None'; + else if (elt[1] === 'L') res.PR = 'Low'; + else if (elt[1] === 'H') res.PR = 'High'; + res.PR = $t(res.PR); + break; + case 'UI': + if (elt[1] === 'N') res.UI = 'None'; + else if (elt[1] === 'R') res.UI = 'Required'; + res.UI = $t(res.UI); + break; + case 'S': + if (elt[1] === 'U') res.S = 'Unchanged'; + else if (elt[1] === 'C') res.S = 'Changed'; + res.S = $t(res.S); + break; + case 'C': + if (elt[1] === 'N') res.C = 'None'; + else if (elt[1] === 'L') res.C = 'Low'; + else if (elt[1] === 'H') res.C = 'High'; + res.C = $t(res.C); + break; + case 'I': + if (elt[1] === 'N') res.I = 'None'; + else if (elt[1] === 'L') res.I = 'Low'; + else if (elt[1] === 'H') res.I = 'High'; + res.I = $t(res.I); + break; + case 'A': + if (elt[1] === 'N') res.A = 'None'; + else if (elt[1] === 'L') res.A = 'Low'; + else if (elt[1] === 'H') res.A = 'High'; + res.A = $t(res.A); + break; + case 'E': + if (elt[1] === 'U') res.E = 'Unproven'; + else if (elt[1] === 'P') res.E = 'Proof-of-Concept'; + else if (elt[1] === 'F') res.E = 'Functional'; + else if (elt[1] === 'H') res.E = 'High'; + res.E = $t(res.E); + break; + case 'RL': + if (elt[1] === 'O') res.RL = 'Official Fix'; + else if (elt[1] === 'T') res.RL = 'Temporary Fix'; + else if (elt[1] === 'W') res.RL = 'Workaround'; + else if (elt[1] === 'U') res.RL = 'Unavailable'; + res.RL = $t(res.RL); + break; + case 'RC': + if (elt[1] === 'U') res.RC = 'Unknown'; + else if (elt[1] === 'R') res.RC = 'Reasonable'; + else if (elt[1] === 'C') res.RC = 'Confirmed'; + res.RC = $t(res.RC); + break; + case 'CR': + if (elt[1] === 'L') res.CR = 'Low'; + else if (elt[1] === 'M') res.CR = 'Medium'; + else if (elt[1] === 'H') res.CR = 'High'; + res.CR = $t(res.CR); + break; + case 'IR': + if (elt[1] === 'L') res.IR = 'Low'; + else if (elt[1] === 'M') res.IR = 'Medium'; + else if (elt[1] === 'H') res.IR = 'High'; + res.IR = $t(res.IR); + break; + case 'AR': + if (elt[1] === 'L') res.AR = 'Low'; + else if (elt[1] === 'M') res.AR = 'Medium'; + else if (elt[1] === 'H') res.AR = 'High'; + res.AR = $t(res.AR); + break; + case 'MAV': + if (elt[1] === 'N') res.MAV = 'Network'; + else if (elt[1] === 'A') res.MAV = 'Adjacent Network'; + else if (elt[1] === 'L') res.MAV = 'Local'; + else if (elt[1] === 'P') res.MAV = 'Physical'; + res.MAV = $t(res.MAV); + break; + case 'MAC': + if (elt[1] === 'L') res.MAC = 'Low'; + else if (elt[1] === 'H') res.MAC = 'High'; + res.MAC = $t(res.MAC); + break; + case 'MPR': + if (elt[1] === 'N') res.MPR = 'None'; + else if (elt[1] === 'L') res.MPR = 'Low'; + else if (elt[1] === 'H') res.MPR = 'High'; + res.MPR = $t(res.MPR); + break; + case 'MUI': + if (elt[1] === 'N') res.MUI = 'None'; + else if (elt[1] === 'R') res.MUI = 'Required'; + res.MUI = $t(res.MUI); + break; + case 'MS': + if (elt[1] === 'U') res.MS = 'Unchanged'; + else if (elt[1] === 'C') res.MS = 'Changed'; + res.MS = $t(res.MS); + break; + case 'MC': + if (elt[1] === 'N') res.MC = 'None'; + else if (elt[1] === 'L') res.MC = 'Low'; + else if (elt[1] === 'H') res.MC = 'High'; + res.MC = $t(res.MC); + break; + case 'MI': + if (elt[1] === 'N') res.MI = 'None'; + else if (elt[1] === 'L') res.MI = 'Low'; + else if (elt[1] === 'H') res.MI = 'High'; + res.MI = $t(res.MI); + break; + case 'MA': + if (elt[1] === 'N') res.MA = 'None'; + else if (elt[1] === 'L') res.MA = 'Low'; + else if (elt[1] === 'H') res.MA = 'High'; + res.MA = $t(res.MA); + break; + default: + break; + } + } + } + return res; +} + +async function prepAuditData(data, settings) { + /** CVSS Colors for table cells */ + var noneColor = settings.report.public.cvssColors.noneColor.replace('#', ''); //default of blue ("#4A86E8") + var lowColor = settings.report.public.cvssColors.lowColor.replace('#', ''); //default of green ("#008000") + var mediumColor = settings.report.public.cvssColors.mediumColor.replace( + '#', + '', + ); //default of yellow ("#f9a009") + var highColor = settings.report.public.cvssColors.highColor.replace('#', ''); //default of red ("#fe0000") + var criticalColor = settings.report.public.cvssColors.criticalColor.replace( + '#', + '', + ); //default of black ("#212121") + + var cellNoneColor = + ''; + var cellLowColor = + ''; + var cellMediumColor = + ''; + var cellHighColor = + ''; + var cellCriticalColor = + ''; + + var result = {}; + result.name = data.name || 'undefined'; + result.auditType = $t(data.auditType) || 'undefined'; + result.date = data.date || 'undefined'; + result.date_start = data.date_start || 'undefined'; + result.date_end = data.date_end || 'undefined'; + if (data.customFields) { + for (var field of data.customFields) { + var fieldType = field.customField.fieldType; + var label = field.customField.label; + + if (fieldType === 'text') + result[_.deburr(label.toLowerCase()).replace(/\s/g, '')] = + await splitHTMLParagraphs(field.text); + else if (fieldType !== 'space') + result[_.deburr(label.toLowerCase()).replace(/\s/g, '')] = field.text; + } + } + + result.company = {}; + if (data.company) { + result.company.name = data.company.name || 'undefined'; + result.company.shortName = data.company.shortName || result.company.name; + result.company.logo = data.company.logo || 'undefined'; + result.company.logo_small = data.company.logo || 'undefined'; + } + + result.client = {}; + if (data.client) { + result.client.email = data.client.email || 'undefined'; + result.client.firstname = data.client.firstname || 'undefined'; + result.client.lastname = data.client.lastname || 'undefined'; + result.client.phone = data.client.phone || 'undefined'; + result.client.cell = data.client.cell || 'undefined'; + result.client.title = data.client.title || 'undefined'; + } + + result.collaborators = []; + data.collaborators.forEach(collab => { + result.collaborators.push({ + username: collab.username || 'undefined', + firstname: collab.firstname || 'undefined', + lastname: collab.lastname || 'undefined', + email: collab.email || 'undefined', + phone: collab.phone || 'undefined', + role: collab.role || 'undefined', + }); + }); + result.language = data.language || 'undefined'; + result.scope = data.scope.toObject() || []; + + result.findings = []; + for (var finding of data.findings) { + var tmpCVSS = CVSS31.calculateCVSSFromVector(finding.cvssv3); + var tmpFinding = { + title: finding.title || '', + vulnType: $t(finding.vulnType) || '', + description: await splitHTMLParagraphs(finding.description), + observation: await splitHTMLParagraphs(finding.observation), + remediation: await splitHTMLParagraphs(finding.remediation), + remediationComplexity: finding.remediationComplexity || '', + priority: finding.priority || '', + references: finding.references || [], + cwes: finding.cwes || [], + poc: await splitHTMLParagraphs(finding.poc), + affected: finding.scope || '', + status: finding.status || '', + category: $t(finding.category) || $t('No Category'), + identifier: 'IDX-' + utils.lPad(finding.identifier), + retestStatus: finding?.retestStatus ? $t(finding.retestStatus) : '', + retestDescription: await splitHTMLParagraphs(finding.retestDescription), + }; + // Handle CVSS + tmpFinding.cvss = { + vectorString: tmpCVSS.vectorString || '', + baseMetricScore: tmpCVSS.baseMetricScore || '', + baseSeverity: tmpCVSS.baseSeverity || '', + temporalMetricScore: tmpCVSS.temporalMetricScore || '', + temporalSeverity: tmpCVSS.temporalSeverity || '', + environmentalMetricScore: tmpCVSS.environmentalMetricScore || '', + environmentalSeverity: tmpCVSS.environmentalSeverity || '', + }; + if (tmpCVSS.baseImpact) + tmpFinding.cvss.baseImpact = CVSS31.roundUp1(tmpCVSS.baseImpact); + else tmpFinding.cvss.baseImpact = ''; + if (tmpCVSS.baseExploitability) + tmpFinding.cvss.baseExploitability = CVSS31.roundUp1( + tmpCVSS.baseExploitability, + ); + else tmpFinding.cvss.baseExploitability = ''; + + if (tmpCVSS.environmentalModifiedImpact) + tmpFinding.cvss.environmentalModifiedImpact = CVSS31.roundUp1( + tmpCVSS.environmentalModifiedImpact, + ); + else tmpFinding.cvss.environmentalModifiedImpact = ''; + if (tmpCVSS.environmentalModifiedExploitability) + tmpFinding.cvss.environmentalModifiedExploitability = CVSS31.roundUp1( + tmpCVSS.environmentalModifiedExploitability, + ); + else tmpFinding.cvss.environmentalModifiedExploitability = ''; + + if (tmpCVSS.baseSeverity === 'Low') + tmpFinding.cvss.cellColor = cellLowColor; + else if (tmpCVSS.baseSeverity === 'Medium') + tmpFinding.cvss.cellColor = cellMediumColor; + else if (tmpCVSS.baseSeverity === 'High') + tmpFinding.cvss.cellColor = cellHighColor; + else if (tmpCVSS.baseSeverity === 'Critical') + tmpFinding.cvss.cellColor = cellCriticalColor; + else tmpFinding.cvss.cellColor = cellNoneColor; + + if (tmpCVSS.temporalSeverity === 'Low') + tmpFinding.cvss.temporalCellColor = cellLowColor; + else if (tmpCVSS.temporalSeverity === 'Medium') + tmpFinding.cvss.temporalCellColor = cellMediumColor; + else if (tmpCVSS.temporalSeverity === 'High') + tmpFinding.cvss.temporalCellColor = cellHighColor; + else if (tmpCVSS.temporalSeverity === 'Critical') + tmpFinding.cvss.temporalCellColor = cellCriticalColor; + else tmpFinding.cvss.temporalCellColor = cellNoneColor; + + if (tmpCVSS.environmentalSeverity === 'Low') + tmpFinding.cvss.environmentalCellColor = cellLowColor; + else if (tmpCVSS.environmentalSeverity === 'Medium') + tmpFinding.cvss.environmentalCellColor = cellMediumColor; + else if (tmpCVSS.environmentalSeverity === 'High') + tmpFinding.cvss.environmentalCellColor = cellHighColor; + else if (tmpCVSS.environmentalSeverity === 'Critical') + tmpFinding.cvss.environmentalCellColor = cellCriticalColor; + else tmpFinding.cvss.environmentalCellColor = cellNoneColor; + + tmpFinding.cvssObj = cvssStrToObject(tmpCVSS.vectorString); + + if (finding.customFields) { + for (field of finding.customFields) { + // For retrocompatibility of findings with old customFields + // or if custom field has been deleted, last saved custom fields will be available + if (field.customField) { + var fieldType = field.customField.fieldType; + var label = field.customField.label; + } else { + var fieldType = field.fieldType; + var label = field.label; + } + if (fieldType === 'text') + tmpFinding[ + _.deburr(label.toLowerCase()) + .replace(/\s/g, '') + .replace(/[^\w]/g, '_') + ] = await splitHTMLParagraphs(field.text); + else if (fieldType !== 'space') + tmpFinding[ + _.deburr(label.toLowerCase()) + .replace(/\s/g, '') + .replace(/[^\w]/g, '_') + ] = field.text; + } + } + result.findings.push(tmpFinding); + } + + result.categories = _.chain(result.findings) + .groupBy('category') + .map((value, key) => { + return { categoryName: key, categoryFindings: value }; + }) + .value(); + + result.creator = {}; + if (data.creator) { + result.creator.username = data.creator.username || 'undefined'; + result.creator.firstname = data.creator.firstname || 'undefined'; + result.creator.lastname = data.creator.lastname || 'undefined'; + result.creator.email = data.creator.email || 'undefined'; + result.creator.phone = data.creator.phone || 'undefined'; + result.creator.role = data.creator.role || 'undefined'; + } + + for (var section of data.sections) { + var formatSection = { + name: $t(section.name), + }; + if (section.text) + // keep text for retrocompatibility + formatSection.text = await splitHTMLParagraphs(section.text); + else if (section.customFields) { + for (field of section.customFields) { + var fieldType = field.customField.fieldType; + var label = field.customField.label; + if (fieldType === 'text') + formatSection[ + _.deburr(label.toLowerCase()) + .replace(/\s/g, '') + .replace(/[^\w]/g, '_') + ] = await splitHTMLParagraphs(field.text); + else if (fieldType !== 'space') + formatSection[ + _.deburr(label.toLowerCase()) + .replace(/\s/g, '') + .replace(/[^\w]/g, '_') + ] = field.text; + } + } + result[section.field] = formatSection; + } + replaceSubTemplating(result); + return result; +} + +async function splitHTMLParagraphs(data) { + var result = []; + if (!data) return result; + + var splitted = data.split(/()/); + + for (var value of splitted) { + if (value.startsWith(' 1) src = src[1]; + if (alt && alt.length > 1) alt = _.unescape(alt[1]); + + if (!src.startsWith('data')) { + try { + src = (await Image.getOne(src)).value; + } catch (error) { + src = 'data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA='; + } + } + if (result.length === 0) result.push({ text: '', images: [] }); + result[result.length - 1].images.push({ image: src, caption: alt }); + } else if (value === '') { + continue; + } else { + result.push({ text: value, images: [] }); + } + } + return result; +} + +function replaceSubTemplating(o, originalData = o) { + var regexp = /\{_\{([a-zA-Z0-9\[\]\_\.]{1,})\}_\}/gm; + if (Array.isArray(o)) + o.forEach(key => replaceSubTemplating(key, originalData)); + else if (typeof o === 'object' && !!o) { + Object.keys(o).forEach(key => { + if (typeof o[key] === 'string') + o[key] = o[key].replace(regexp, (match, word) => + _.get(originalData, word.trim(), ''), + ); + else replaceSubTemplating(o[key], originalData); + }); + } +} diff --git a/backend/src/lib/utils.js b/backend/src/lib/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..89b74c6963b67342e466ea1f4ed2f40e734701c9 --- /dev/null +++ b/backend/src/lib/utils.js @@ -0,0 +1,58 @@ +// Filename whitelist validation for template creation +function validFilename(filename) { + const regex = /^[\p{Letter}\p{Mark}0-9 \[\]'()_-]+$/iu; + + return regex.test(filename); +} +exports.validFilename = validFilename; + +// Escape XML special entities when using {@RawXML} in template generation +function escapeXMLEntities(input) { + var XML_CHAR_MAP = { '<': '<', '>': '>', '&': '&' }; + var standardEncode = input.replace(/[<>&]/g, function (ch) { + return XML_CHAR_MAP[ch]; + }); + return standardEncode; +} +exports.escapeXMLEntities = escapeXMLEntities; + +// Convert number to 3 digits format if under 100 +function lPad(number) { + if (number <= 99) { + number = ('00' + number).slice(-3); + } + return `${number}`; +} +exports.lPad = lPad; + +function escapeRegex(regex) { + return regex.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); +} +exports.escapeRegex = escapeRegex; + +function generateUUID() { + return require('crypto').randomBytes(32).toString('hex'); +} +exports.generateUUID = generateUUID; + +var getObjectPaths = (obj, prefix = '') => + Object.keys(obj).reduce((res, el) => { + if (Array.isArray(obj[el])) { + return [...res, prefix + el]; + } else if (typeof obj[el] === 'object' && obj[el] !== null) { + return [...res, ...getObjectPaths(obj[el], prefix + el + '.')]; + } + return [...res, prefix + el]; + }, []); +exports.getObjectPaths = getObjectPaths; + +function getSockets(io, room) { + var result = []; + io.sockets.sockets.forEach(data => { + if (data.rooms.has(room)) { + result.push(data); + } + }); + return result; +} +exports.getSockets = getSockets; diff --git a/backend/src/models/audit-type.js b/backend/src/models/audit-type.js new file mode 100644 index 0000000000000000000000000000000000000000..9bda8be6b6a4bc6675a84c3feee27b0b95229c46 --- /dev/null +++ b/backend/src/models/audit-type.js @@ -0,0 +1,114 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var Template = { + _id: false, + template: { type: Schema.Types.ObjectId, ref: 'Template' }, + locale: String, +}; + +var AuditTypeSchema = new Schema( + { + name: { type: String, unique: true }, + templates: [Template], + sections: [{ type: String, ref: 'CustomSection' }], + hidden: [{ type: String, enum: ['network', 'findings'] }], + stage: { + type: String, + enum: ['default', 'retest', 'multi'], + default: 'default', + }, + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get all auditTypes +AuditTypeSchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = AuditType.find(); + query.select('_id name templates sections hidden stage'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get auditType by name +AuditTypeSchema.statics.getByName = name => { + return new Promise((resolve, reject) => { + var query = AuditType.findOne({ name: name }); + query.select('-_id name templates sections hidden stage'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create auditType +AuditTypeSchema.statics.create = auditType => { + return new Promise((resolve, reject) => { + var query = new AuditType(auditType); + query + .save() + .then(row => { + resolve(row); + }) + .catch(err => { + if (err.code === 11000) + reject({ fn: 'BadParameters', message: 'Audit Type already exists' }); + else reject(err); + }); + }); +}; + +// Update Audit Types +AuditTypeSchema.statics.updateAll = auditTypes => { + return new Promise((resolve, reject) => { + AuditType.deleteMany() + .then(row => { + AuditType.insertMany(auditTypes); + }) + .then(row => { + resolve('Audit Types updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Delete auditType +AuditTypeSchema.statics.delete = name => { + return new Promise((resolve, reject) => { + AuditType.deleteOne({ name: name }) + .then(res => { + if (res.deletedCount === 1) resolve('Audit Type deleted'); + else reject({ fn: 'NotFound', message: 'Audit Type not found' }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var AuditType = mongoose.model('AuditType', AuditTypeSchema); +AuditType.syncIndexes(); +module.exports = AuditType; diff --git a/backend/src/models/audit.js b/backend/src/models/audit.js new file mode 100644 index 0000000000000000000000000000000000000000..b5d14eecd63ea987e9c5cebf64b31755e8de7cc6 --- /dev/null +++ b/backend/src/models/audit.js @@ -0,0 +1,1195 @@ +var mongoose = require('mongoose'); //.set('debug', true); +const CVSS31 = require('../lib/cvsscalc31'); +var Schema = mongoose.Schema; + +var Paragraph = { + text: String, + images: [{ image: String, caption: String }], +}; + +var customField = { + _id: false, + customField: { type: Schema.Types.Mixed, ref: 'CustomField' }, + text: Schema.Types.Mixed, +}; + +var Finding = { + id: Schema.Types.ObjectId, + identifier: Number, //incremental ID to be shown in the report + title: String, + vulnType: String, + description: String, + observation: String, + remediation: String, + remediationComplexity: { type: Number, enum: [1, 2, 3] }, + priority: { type: Number, enum: [1, 2, 3, 4] }, + references: [String], + cwes: [String], + cvssv3: String, + paragraphs: [Paragraph], + poc: String, + scope: String, + status: { type: Number, enum: [0, 1], default: 1 }, // 0: done, 1: redacting + category: String, + customFields: [customField], + retestStatus: { type: String, enum: ['ok', 'ko', 'unknown', 'partial'] }, + retestDescription: String, +}; + +var Service = { + port: Number, + protocol: { type: String, enum: ['tcp', 'udp'] }, + name: String, + product: String, + version: String, +}; + +var Host = { + hostname: String, + ip: String, + os: String, + services: [Service], +}; + +var SortOption = { + _id: false, + category: String, + sortValue: String, + sortOrder: { type: String, enum: ['desc', 'asc'] }, + sortAuto: Boolean, +}; + +var AuditSchema = new Schema( + { + name: { type: String, required: true }, + auditType: String, + date: String, + date_start: String, + date_end: String, + summary: String, + company: { type: Schema.Types.ObjectId, ref: 'Company' }, + client: { type: Schema.Types.ObjectId, ref: 'Client' }, + collaborators: [{ type: Schema.Types.ObjectId, ref: 'User' }], + reviewers: [{ type: Schema.Types.ObjectId, ref: 'User' }], + language: { type: String, required: true }, + scope: [{ _id: false, name: String, hosts: [Host] }], + findings: [Finding], + template: { type: Schema.Types.ObjectId, ref: 'Template' }, + creator: { type: Schema.Types.ObjectId, ref: 'User' }, + sections: [ + { + field: String, + name: String, + text: String, + customFields: [customField], + }, + ], // keep text for retrocompatibility + customFields: [customField], + sortFindings: [SortOption], + state: { + type: String, + enum: ['EDIT', 'REVIEW', 'APPROVED'], + default: 'EDIT', + }, + approvals: [{ type: Schema.Types.ObjectId, ref: 'User' }], + type: { + type: String, + enum: ['default', 'multi', 'retest'], + default: 'default', + }, + parentId: { type: Schema.Types.ObjectId, ref: 'Audit' }, + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get all audits (admin) +AuditSchema.statics.getAudits = (isAdmin, userId, filters) => { + return new Promise((resolve, reject) => { + var query = Audit.find(filters); + if (!isAdmin) + query.or([ + { creator: userId }, + { collaborators: userId }, + { reviewers: userId }, + ]); + query.populate('creator', 'username'); + query.populate('collaborators', 'username'); + query.populate('reviewers', 'username firstname lastname'); + query.populate('approvals', 'username firstname lastname'); + query.populate('company', 'name'); + query.populate('template', '-_id ext'); + query.select( + 'id name auditType language creator collaborators company createdAt state type parentId template', + ); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get Audit with ID to generate report +AuditSchema.statics.getAudit = (isAdmin, auditId, userId) => { + return new Promise((resolve, reject) => { + var query = Audit.findById(auditId); + if (!isAdmin) + query.or([ + { creator: userId }, + { collaborators: userId }, + { reviewers: userId }, + ]); + query.populate('template'); + query.populate('creator', 'username firstname lastname email phone role'); + query.populate('company'); + query.populate('client'); + query.populate( + 'collaborators', + 'username firstname lastname email phone role', + ); + query.populate('reviewers', 'username firstname lastname role'); + query.populate('approvals', 'username firstname lastname role'); + query.populate('customFields.customField', 'label fieldType text'); + query.populate({ + path: 'findings', + populate: { + path: 'customFields.customField', + select: 'label fieldType text', + }, + }); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + resolve(row); + }) + .catch(err => { + if (err.name === 'CastError') + reject({ fn: 'BadParameters', message: 'Bad Audit Id' }); + else reject(err); + }); + }); +}; + +AuditSchema.statics.getAuditChildren = (isAdmin, auditId, userId) => { + return new Promise((resolve, reject) => { + var query = Audit.find({ parentId: auditId }); + if (!isAdmin) + query.or([ + { creator: userId }, + { collaborators: userId }, + { reviewers: userId }, + ]); + query + .exec() + .then(rows => { + if (!rows) + throw { + fn: 'NotFound', + message: 'Children not found or Insufficient Privileges', + }; + resolve(rows); + }) + .catch(err => { + if (err.name === 'CastError') + reject({ fn: 'BadParameters', message: 'Bad Audit Id' }); + else reject(err); + }); + }); +}; + +// Get Audit Retest +AuditSchema.statics.getRetest = (isAdmin, auditId, userId) => { + return new Promise((resolve, reject) => { + var query = Audit.findOne({ parentId: auditId }); + + if (!isAdmin) + query.or([ + { creator: userId }, + { collaborators: userId }, + { reviewers: userId }, + ]); + query + .exec() + .then(row => { + if (!row) + throw { fn: 'NotFound', message: 'No retest found for this audit' }; + else { + resolve(row); + } + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create Audit Retest +AuditSchema.statics.createRetest = (isAdmin, auditId, userId, auditType) => { + return new Promise((resolve, reject) => { + var audit = {}; + audit.creator = userId; + audit.type = 'retest'; + audit.parentId = auditId; + audit.auditType = auditType; + audit.findings = []; + audit.sections = []; + audit.customFields = []; + + var auditTypeSections = []; + var customSections = []; + var customFields = []; + var AuditType = mongoose.model('AuditType'); + + var query = Audit.findById(auditId); + if (!isAdmin) + query.or([ + { creator: userId }, + { collaborators: userId }, + { reviewers: userId }, + ]); + query + .exec() + .then(async row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + else { + var retest = await Audit.findOne({ parentId: auditId }).exec(); + if (retest) + throw { + fn: 'BadParameters', + message: 'Retest already exists for this Audit', + }; + audit.name = row.name; + audit.company = row.company; + audit.client = row.client; + audit.collaborators = row.collaborators; + audit.reviewers = row.reviewers; + audit.language = row.language; + audit.scope = row.scope; + audit.findings = row.findings; + // row.findings.forEach(finding => { + // var tmpFinding = {} + // tmpFinding.title = finding.title + // tmpFinding.identifier = finding.identifier + // tmpFinding.cvssv3 = finding.cvssv3 + // tmpFinding.vulnType = finding.vulnType + // tmpFinding.category = finding.category + // audit.findings.push(tmpFinding) + // }) + return AuditType.getByName(auditType); + } + }) + .then(row => { + if (row) { + auditTypeSections = row.sections; + var auditTypeTemplate = row.templates.find( + e => e.locale === audit.language, + ); + if (auditTypeTemplate) audit.template = auditTypeTemplate.template; + var Section = mongoose.model('CustomSection'); + var CustomField = mongoose.model('CustomField'); + var promises = []; + promises.push(Section.getAll()); + promises.push(CustomField.getAll()); + return Promise.all(promises); + } else throw { fn: 'NotFound', message: 'AuditType not found' }; + }) + .then(resolved => { + customSections = resolved[0]; + customFields = resolved[1]; + + customSections.forEach(section => { + // Add sections with customFields (and default text) to audit + var tmpSection = {}; + if (auditTypeSections.includes(section.field)) { + tmpSection.field = section.field; + tmpSection.name = section.name; + tmpSection.customFields = []; + + customFields.forEach(field => { + field = field.toObject(); + if ( + field.display === 'section' && + field.displaySub === tmpSection.name + ) { + var fieldText = field.text.find( + e => e.locale === audit.language, + ); + if (fieldText) fieldText = fieldText.value; + else fieldText = ''; + + delete field.text; + tmpSection.customFields.push({ + customField: field, + text: fieldText, + }); + } + }); + audit.sections.push(tmpSection); + } + }); + + customFields.forEach(field => { + // Add customFields (and default text) to audit + field = field.toObject(); + if (field.display === 'general') { + var fieldText = field.text.find(e => e.locale === audit.language); + if (fieldText) fieldText = fieldText.value; + else fieldText = ''; + + delete field.text; + audit.customFields.push({ customField: field, text: fieldText }); + } + }); + + return new Audit(audit).save(); + }) + .then(rows => { + resolve(rows); + }) + .catch(err => { + console.log(err); + if (err.name === 'ValidationError') + reject({ fn: 'BadParameters', message: 'Audit validation failed' }); + else reject(err); + }); + }); +}; + +// Create audit +AuditSchema.statics.create = (audit, userId) => { + return new Promise((resolve, reject) => { + audit.creator = userId; + audit.sections = []; + audit.customFields = []; + + var auditTypeSections = []; + var customSections = []; + var customFields = []; + var AuditType = mongoose.model('AuditType'); + AuditType.getByName(audit.auditType) + .then(row => { + if (row) { + auditTypeSections = row.sections; + var auditTypeTemplate = row.templates.find( + e => e.locale === audit.language, + ); + if (auditTypeTemplate) audit.template = auditTypeTemplate.template; + var Section = mongoose.model('CustomSection'); + var CustomField = mongoose.model('CustomField'); + var promises = []; + promises.push(Section.getAll()); + promises.push(CustomField.getAll()); + return Promise.all(promises); + } else throw { fn: 'NotFound', message: 'AuditType not found' }; + }) + .then(resolved => { + customSections = resolved[0]; + customFields = resolved[1]; + + customSections.forEach(section => { + // Add sections with customFields (and default text) to audit + var tmpSection = {}; + if (auditTypeSections.includes(section.field)) { + tmpSection.field = section.field; + tmpSection.name = section.name; + tmpSection.customFields = []; + + customFields.forEach(field => { + field = field.toObject(); + if ( + field.display === 'section' && + field.displaySub === tmpSection.name + ) { + var fieldText = field.text.find( + e => e.locale === audit.language, + ); + if (fieldText) fieldText = fieldText.value; + else fieldText = ''; + + delete field.text; + tmpSection.customFields.push({ + customField: field, + text: fieldText, + }); + } + }); + audit.sections.push(tmpSection); + } + }); + + customFields.forEach(field => { + // Add customFields (and default text) to audit + field = field.toObject(); + if (field.display === 'general') { + var fieldText = field.text.find(e => e.locale === audit.language); + if (fieldText) fieldText = fieldText.value; + else fieldText = ''; + + delete field.text; + audit.customFields.push({ customField: field, text: fieldText }); + } + }); + + var VulnerabilityCategory = mongoose.model('VulnerabilityCategory'); + return VulnerabilityCategory.getAll(); + }) + .then(rows => { + // Add default sort options for each vulnerability category + audit.sortFindings = []; + rows.forEach(e => { + audit.sortFindings.push({ + category: e.name, + sortValue: e.sortValue, + sortOrder: e.sortOrder, + sortAuto: e.sortAuto, + }); + }); + + return new Audit(audit).save(); + }) + .then(rows => { + resolve(rows); + }) + .catch(err => { + console.log(err); + if (err.name === 'ValidationError') + reject({ fn: 'BadParameters', message: 'Audit validation failed' }); + else reject(err); + }); + }); +}; + +// Delete audit +AuditSchema.statics.delete = (isAdmin, auditId, userId) => { + return new Promise((resolve, reject) => { + var query = Audit.findOneAndDelete({ _id: auditId }); + if (!isAdmin) query.or([{ creator: userId }]); + return query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + resolve(row); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get audit general information +AuditSchema.statics.getGeneral = (isAdmin, auditId, userId) => { + return new Promise((resolve, reject) => { + var query = Audit.findById(auditId); + if (!isAdmin) + query.or([ + { creator: userId }, + { collaborators: userId }, + { reviewers: userId }, + ]); + query.populate({ + path: 'client', + select: 'email firstname lastname', + populate: { + path: 'company', + select: 'name', + }, + }); + query.populate('creator', 'username firstname lastname'); + query.populate('collaborators', 'username firstname lastname'); + query.populate('reviewers', 'username firstname lastname'); + query.populate('company'); + query.select( + 'name auditType date date_start date_end client collaborators language scope.name template customFields', + ); + query + .lean() + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + var formatScope = row.scope.map(item => { + return item.name; + }); + for (var i = 0; i < formatScope.length; i++) { + row.scope[i] = formatScope[i]; + } + resolve(row); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Update audit general information +AuditSchema.statics.updateGeneral = (isAdmin, auditId, userId, update) => { + return new Promise(async (resolve, reject) => { + if (update.company && update.company.name) { + var Company = mongoose.model('Company'); + try { + update.company = await Company.create({ name: update.company.name }); + } catch (error) { + console.log(error); + delete update.company; + } + } + var query = Audit.findByIdAndUpdate(auditId, update); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + resolve('Audit General updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get audit Network information +AuditSchema.statics.getNetwork = (isAdmin, auditId, userId) => { + return new Promise((resolve, reject) => { + var query = Audit.findById(auditId); + if (!isAdmin) + query.or([ + { creator: userId }, + { collaborators: userId }, + { reviewers: userId }, + ]); + query.select('scope'); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + resolve(row); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Update audit Network information +AuditSchema.statics.updateNetwork = (isAdmin, auditId, userId, scope) => { + return new Promise((resolve, reject) => { + var query = Audit.findByIdAndUpdate(auditId, scope); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + resolve('Audit Network updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create finding +AuditSchema.statics.createFinding = (isAdmin, auditId, userId, finding) => { + return new Promise((resolve, reject) => { + Audit.getLastFindingIdentifier(auditId).then(identifier => { + finding.identifier = ++identifier; + + var query = Audit.findByIdAndUpdate(auditId, { + $push: { findings: finding }, + }); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + return query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + else { + var sortOption = row.sortFindings.find( + e => e.category === (finding.category || 'No Category'), + ); + if ((sortOption && sortOption.sortAuto) || !sortOption) + // if sort is set to automatic or undefined then we sort (default sort will be applied to undefined sortOption) + return Audit.updateSortFindings(isAdmin, auditId, userId, null); + // if manual sorting then we do not sort + else resolve('Audit Finding created succesfully'); + } + }) + .then(() => { + resolve('Audit Finding created successfully'); + }) + .catch(err => { + reject(err); + }); + }); + }); +}; + +AuditSchema.statics.getLastFindingIdentifier = auditId => { + return new Promise((resolve, reject) => { + var query = Audit.aggregate([ + { $match: { _id: new mongoose.Types.ObjectId(auditId) } }, + ]); + query.unwind('findings'); + query.sort({ 'findings.identifier': -1 }); + query + .exec() + .then(row => { + if (!row) throw { fn: 'NotFound', message: 'Audit not found' }; + else if (row.length === 0 || !row[0].findings.identifier) resolve(0); + else resolve(row[0].findings.identifier); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get finding of audit +AuditSchema.statics.getFinding = (isAdmin, auditId, userId, findingId) => { + return new Promise((resolve, reject) => { + var query = Audit.findById(auditId); + if (!isAdmin) + query.or([ + { creator: userId }, + { collaborators: userId }, + { reviewers: userId }, + ]); + query.select('findings'); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + var finding = row.findings.id(findingId); + if (finding === null) + throw { fn: 'NotFound', message: 'Finding not found' }; + else resolve(finding); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Update finding of audit +AuditSchema.statics.updateFinding = ( + isAdmin, + auditId, + userId, + findingId, + newFinding, +) => { + return new Promise((resolve, reject) => { + var sortAuto = true; + + var query = Audit.findById(auditId); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + var finding = row.findings.id(findingId); + if (finding === null) + reject({ fn: 'NotFound', message: 'Finding not found' }); + else { + var sortOption = row.sortFindings.find( + e => e.category === (newFinding.category || 'No Category'), + ); + if (sortOption && !sortOption.sortAuto) sortAuto = false; + + Object.keys(newFinding).forEach(key => { + finding[key] = newFinding[key]; + }); + return row.save({ validateBeforeSave: false }); // Disable schema validation since scope changed from Array to String + } + }) + .then(() => { + if (sortAuto) + return Audit.updateSortFindings(isAdmin, auditId, userId, null); + else resolve('Audit Finding updated successfully'); + }) + .then(() => { + resolve('Audit Finding updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Delete finding of audit +AuditSchema.statics.deleteFinding = (isAdmin, auditId, userId, findingId) => { + return new Promise((resolve, reject) => { + var query = Audit.findById(auditId); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query.select('findings'); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + var finding = row.findings.id(findingId); + if (finding === null) + reject({ fn: 'NotFound', message: 'Finding not found' }); + else { + row.findings.pull(findingId); + return row.save(); + } + }) + .then(() => { + resolve('Audit Finding deleted successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create section +AuditSchema.statics.createSection = (isAdmin, auditId, userId, section) => { + return new Promise((resolve, reject) => { + var query = Audit.findOneAndUpdate( + { _id: auditId, 'sections.field': { $ne: section.field } }, + { $push: { sections: section } }, + ); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: + 'Audit not found or Section already exists or Insufficient Privileges', + }; + + resolve('Audit Section created successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get section of audit +AuditSchema.statics.getSection = (isAdmin, auditId, userId, sectionId) => { + return new Promise((resolve, reject) => { + var query = Audit.findById(auditId); + if (!isAdmin) + query.or([ + { creator: userId }, + { collaborators: userId }, + { reviewers: userId }, + ]); + + query.select('sections'); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + var section = row.sections.id(sectionId); + if (section === null) + throw { fn: 'NotFound', message: 'Section id not found' }; + else resolve(section); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Update section of audit +AuditSchema.statics.updateSection = ( + isAdmin, + auditId, + userId, + sectionId, + newSection, +) => { + return new Promise((resolve, reject) => { + var query = Audit.findById(auditId); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + var section = row.sections.id(sectionId); + if (section === null) + throw { fn: 'NotFound', message: 'Section not found' }; + else { + Object.keys(newSection).forEach(key => { + section[key] = newSection[key]; + }); + return row.save(); + } + }) + .then(() => { + resolve('Audit Section updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Delete section of audit +AuditSchema.statics.deleteSection = (isAdmin, auditId, userId, sectionId) => { + return new Promise((resolve, reject) => { + var query = Audit.findById(auditId); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query.select('sections'); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + var section = row.sections.id(sectionId); + if (section === null) + throw { fn: 'NotFound', message: 'Section not found' }; + else { + row.sections.pull(sectionId); + return row.save(); + } + }) + .then(() => { + resolve('Audit Section deleted successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Update audit sort options for findings and run the sorting. If update param is null then just run sorting +(AuditSchema.statics.updateSortFindings = ( + isAdmin, + auditId, + userId, + update, +) => { + return new Promise((resolve, reject) => { + var audit = {}; + var query = Audit.findById(auditId); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + else { + audit = row; + if (update) + // if update is null then we only sort findings (no sort options saving) + audit.sortFindings = update.sortFindings; // saving sort options to audit + + var VulnerabilityCategory = mongoose.model('VulnerabilityCategory'); + return VulnerabilityCategory.getAll(); + } + }) + .then(row => { + var _ = require('lodash'); + var findings = []; + var categoriesOrder = row.map(e => e.name); + categoriesOrder.push('undefined'); // Put uncategorized findings at the end + + // Group findings by category + var findingList = _.chain(audit.findings) + .groupBy('category') + .toPairs() + .sort( + (a, b) => + categoriesOrder.indexOf(a[0]) - categoriesOrder.indexOf(b[0]), + ) + .fromPairs() + .map((value, key) => { + if (key === 'undefined') key = 'No Category'; + var sortOption = audit.sortFindings.find( + option => option.category === key, + ); // Get sort option saved in audit + if (!sortOption) + // no option for category in audit + sortOption = row.find(e => e.name === key); // Get sort option from default in vulnerability category + if (!sortOption) + // no default option or category don't exist + sortOption = { + sortValue: 'cvssScore', + sortOrder: 'desc', + sortAuto: true, + }; // set a default sort option + + return { category: key, findings: value, sortOption: sortOption }; + }) + .value(); + + findingList.forEach(group => { + var order = -1; // desc + if (group.sortOption.sortOrder === 'asc') order = 1; + + var tmpFindings = group.findings.sort((a, b) => { + var cvssA = CVSS31.calculateCVSSFromVector(a.cvssv3); + var cvssB = CVSS31.calculateCVSSFromVector(b.cvssv3); + + // Get built-in value (findings[sortValue]) + var left = a[group.sortOption.sortValue]; + + // If sort value is a CVSS Score calculate it + if (cvssA.success && group.sortOption.sortValue === 'cvssScore') + left = cvssA.baseMetricScore; + else if ( + cvssA.success && + group.sortOption.sortValue === 'cvssTemporalScore' + ) + left = cvssA.temporalMetricScore; + else if ( + cvssA.success && + group.sortOption.sortValue === 'cvssEnvironmentalScore' + ) + left = cvssA.environmentalMetricScore; + + // Not found then get customField sortValue + if (!left) { + left = a.customFields.find( + e => e.customField.label === group.sortOption.sortValue, + ); + if (left) left = left.text; + } + // Not found then set default to 0 + if (!left) left = 0; + // Convert to string in case of int value + left = left.toString(); + + // Same for right value to compare + var right = b[group.sortOption.sortValue]; + + if (cvssB.success && group.sortOption.sortValue === 'cvssScore') + right = cvssB.baseMetricScore; + else if ( + cvssB.success && + group.sortOption.sortValue === 'cvssTemporalScore' + ) + right = cvssB.temporalMetricScore; + else if ( + cvssB.success && + group.sortOption.sortValue === 'cvssEnvironmentalScore' + ) + right = cvssB.environmentalMetricScore; + + if (!right) { + right = b.customFields.find( + e => e.customField.label === group.sortOption.sortValue, + ); + if (right) right = right.text; + } + if (!right) right = 0; + right = right.toString(); + return ( + left.localeCompare(right, undefined, { numeric: true }) * order + ); + }); + + findings = findings.concat(tmpFindings); + }); + + audit.findings = findings; + + return audit.save(); + }) + .then(() => { + resolve('Audit findings sorted successfully'); + }) + .catch(err => { + console.log(err); + reject(err); + }); + }); +}), + // Move finding from move.oldIndex to move.newIndex + (AuditSchema.statics.moveFindingPosition = ( + isAdmin, + auditId, + userId, + move, + ) => { + return new Promise((resolve, reject) => { + var query = Audit.findById(auditId); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + var tmp = row.findings[move.oldIndex]; + row.findings.splice(move.oldIndex, 1); + row.findings.splice(move.newIndex, 0, tmp); + + row.markModified('findings'); + return row.save(); + }) + .then(msg => { + resolve('Audit Finding moved successfully'); + }) + .catch(err => { + reject(err); + }); + }); + }); + +AuditSchema.statics.updateApprovals = (isAdmin, auditId, userId, update) => { + return new Promise(async (resolve, reject) => { + var Settings = mongoose.model('Settings'); + var settings = await Settings.getAll(); + + if (update.approvals.length >= settings.reviews.public.minReviewers) { + update.state = 'APPROVED'; + } else { + update.state = 'REVIEW'; + } + + var query = Audit.findByIdAndUpdate(auditId, update); + query.nor([{ creator: userId }, { collaborators: userId }]); + if (!isAdmin) query.or([{ reviewers: userId }]); + + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + resolve('Audit approvals updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Update audit parent +AuditSchema.statics.updateParent = (isAdmin, auditId, userId, parentId) => { + return new Promise(async (resolve, reject) => { + var query = Audit.findByIdAndUpdate(auditId, { parentId: parentId }); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + resolve('Audit Parent updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Delete audit parent +AuditSchema.statics.deleteParent = (isAdmin, auditId, userId) => { + return new Promise(async (resolve, reject) => { + var query = Audit.findByIdAndUpdate(auditId, { parentId: null }); + if (!isAdmin) query.or([{ creator: userId }, { collaborators: userId }]); + query + .exec() + .then(row => { + if (!row) + throw { + fn: 'NotFound', + message: 'Audit not found or Insufficient Privileges', + }; + + resolve(row); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var Audit = mongoose.model('Audit', AuditSchema); +// Audit.syncIndexes() +module.exports = Audit; diff --git a/backend/src/models/client.js b/backend/src/models/client.js new file mode 100644 index 0000000000000000000000000000000000000000..4ac9f39228fcfab4961ecb7cc8c1489db18f786f --- /dev/null +++ b/backend/src/models/client.js @@ -0,0 +1,128 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var ClientSchema = new Schema( + { + email: { type: String, required: true, unique: true }, + company: { type: Schema.Types.ObjectId, ref: 'Company' }, + lastname: String, + firstname: String, + phone: String, + cell: String, + title: String, + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get all clients +ClientSchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = Client.find().populate('company', '-_id name'); + query.select('email lastname firstname phone cell title'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create client +ClientSchema.statics.create = (client, company) => { + return new Promise(async (resolve, reject) => { + if (company) { + var Company = mongoose.model('Company'); + var query = Company.findOneAndUpdate( + { name: company }, + {}, + { upsert: true, new: true }, + ); + var companyRow = await query.exec(); + if (companyRow) client.company = companyRow._id; + } + var query = new Client(client); + query + .save(company) + .then(row => { + resolve({ + _id: row._id, + email: row.email, + firstname: row.firstname, + lastname: row.lastname, + title: row.title, + phone: row.phone, + cell: row.cell, + company: row.company, + }); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Client email already exists', + }); + else reject(err); + }); + }); +}; + +// Update client +ClientSchema.statics.update = (clientId, client, company) => { + return new Promise(async (resolve, reject) => { + if (company) { + var Company = mongoose.model('Company'); + var query = Company.findOneAndUpdate( + { name: company }, + {}, + { upsert: true, new: true }, + ); + var companyRow = await query.exec(); + if (companyRow) client.company = companyRow.id; + } + var query = Client.findOneAndUpdate({ _id: clientId }, client); + query + .exec() + .then(rows => { + if (rows) resolve(rows); + else reject({ fn: 'NotFound', message: 'Client Id not found' }); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Client email already exists', + }); + else reject(err); + }); + }); +}; + +// Delete client +ClientSchema.statics.delete = clientId => { + return new Promise((resolve, reject) => { + var query = Client.findOneAndDelete({ _id: clientId }); + query + .exec() + .then(rows => { + if (rows) resolve(rows); + else reject({ fn: 'NotFound', message: 'Client Id not found' }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var Client = mongoose.model('Client', ClientSchema); +module.exports = Client; diff --git a/backend/src/models/company.js b/backend/src/models/company.js new file mode 100644 index 0000000000000000000000000000000000000000..e5b748b8daeca4a78fc3a52b56b319706e9c1dfc --- /dev/null +++ b/backend/src/models/company.js @@ -0,0 +1,95 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var CompanySchema = new Schema( + { + name: { type: String, required: true, unique: true }, + shortName: String, + logo: String, + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get all companies +CompanySchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = Company.find(); + query.select('name shortName logo'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create company +CompanySchema.statics.create = company => { + return new Promise((resolve, reject) => { + var query = new Company(company); + query + .save(company) + .then(row => { + resolve({ _id: row._id, name: row.name }); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Company name already exists', + }); + else reject(err); + }); + }); +}; + +// Update company +CompanySchema.statics.update = (companyId, company) => { + return new Promise((resolve, reject) => { + var query = Company.findOneAndUpdate({ _id: companyId }, company); + query + .exec() + .then(rows => { + if (rows) resolve(rows); + else reject({ fn: 'NotFound', message: 'Company Id not found' }); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Company name already exists', + }); + else reject(err); + }); + }); +}; + +// Delete company +CompanySchema.statics.delete = companyId => { + return new Promise((resolve, reject) => { + var query = Company.findOneAndDelete({ _id: companyId }); + query + .exec() + .then(rows => { + if (rows) resolve(rows); + else reject({ fn: 'NotFound', message: 'Company Id not found' }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var Company = mongoose.model('Company', CompanySchema); +module.exports = Company; diff --git a/backend/src/models/custom-field.js b/backend/src/models/custom-field.js new file mode 100644 index 0000000000000000000000000000000000000000..99d2eddbeb0bd058cb301c2ddc1635e518441d46 --- /dev/null +++ b/backend/src/models/custom-field.js @@ -0,0 +1,147 @@ +var mongoose = require('mongoose'); //.set('debug', true); +var Schema = mongoose.Schema; + +var CustomFieldSchema = new Schema( + { + fieldType: String, + label: String, + display: String, + displaySub: { type: String, default: '' }, + position: Number, + size: { + type: Number, + enum: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + default: 12, + }, + offset: { + type: Number, + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + default: 0, + }, + required: { type: Boolean, default: false }, + description: { type: String, default: '' }, + text: [{ _id: false, locale: String, value: Schema.Types.Mixed }], + options: [{ _id: false, locale: String, value: String }], + }, + { timestamps: true }, +); + +CustomFieldSchema.index( + { label: 1, display: 1, displaySub: 1 }, + { + name: 'unique_label_display', + unique: true, + partialFilterExpression: { label: { $exists: true, $gt: '' } }, + }, +); + +/* + *** Statics *** + */ + +// Get all Fields +CustomFieldSchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = CustomField.find().sort('position'); + query.select( + 'fieldType label display displaySub size offset required description text options', + ); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create Field +CustomFieldSchema.statics.create = field => { + return new Promise((resolve, reject) => { + var query = new CustomField(field); + query + .save() + .then(row => { + resolve(row); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Custom Field already exists', + }); + else reject(err); + }); + }); +}; + +// Update Fields +CustomFieldSchema.statics.updateAll = fields => { + return new Promise((resolve, reject) => { + var promises = fields.map(field => { + return CustomField.findByIdAndUpdate(field._id, field).exec(); + }); + return Promise.all(promises) + .then(row => { + resolve('Fields updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Delete Field +CustomFieldSchema.statics.delete = fieldId => { + return new Promise((resolve, reject) => { + var pullCount = 0; + var Vulnerability = mongoose.model('Vulnerability'); + var query = Vulnerability.find(); + query + .exec() + .then(rows => { + var promises = []; + promises.push(CustomField.findByIdAndDelete(fieldId).exec()); + rows.map(row => { + row.details.map(detail => { + if ( + detail.customFields.some( + field => `${field.customField}` === fieldId, + ) + ) + pullCount++; + + detail.customFields.pull({ customField: fieldId }); + }); + promises.push(row.save()); + }); + return Promise.all(promises); + }) + .then(row => { + if (row && row[0]) + resolve({ + msg: `Custom Field deleted successfully`, + vulnCount: pullCount, + }); + else + reject({ + fn: 'NotFound', + message: { msg: 'Custom Field not found', vulnCount: pullCount }, + }); + }) + .catch(err => { + console.log(err); + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var CustomField = mongoose.model('CustomField', CustomFieldSchema); +CustomField.syncIndexes(); +module.exports = CustomField; diff --git a/backend/src/models/custom-section.js b/backend/src/models/custom-section.js new file mode 100644 index 0000000000000000000000000000000000000000..0f5c7c9919178bd48b5148ac96d403981f02ac6b --- /dev/null +++ b/backend/src/models/custom-section.js @@ -0,0 +1,105 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var CustomSectionSchema = new Schema( + { + field: { type: String, required: true, unique: true }, + name: { type: String, required: true, unique: true }, + icon: String, + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get all Sections +CustomSectionSchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = CustomSection.find(); + query.select('-_id field name icon'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get all Sections by Language +CustomSectionSchema.statics.getAllByLanguage = locale => { + return new Promise((resolve, reject) => { + var query = CustomSection.find({ locale: locale }); + query.select('-_id field name icon'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create Section +CustomSectionSchema.statics.create = section => { + return new Promise((resolve, reject) => { + var query = new CustomSection(section); + query + .save() + .then(row => { + resolve(row); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Custom Section already exists', + }); + else reject(err); + }); + }); +}; + +// Update Sections +CustomSectionSchema.statics.updateAll = sections => { + return new Promise((resolve, reject) => { + CustomSection.deleteMany() + .then(row => { + CustomSection.insertMany(sections); + }) + .then(row => { + resolve('Sections updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Delete Section +CustomSectionSchema.statics.delete = (field, locale) => { + return new Promise((resolve, reject) => { + CustomSection.deleteOne({ field: field, locale: locale }) + .then(res => { + if (res.deletedCount === 1) resolve('Custom Section deleted'); + else reject({ fn: 'NotFound', message: 'Custom Section not found' }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var CustomSection = mongoose.model('CustomSection', CustomSectionSchema); +CustomSection.syncIndexes(); +module.exports = CustomSection; diff --git a/backend/src/models/image.js b/backend/src/models/image.js new file mode 100644 index 0000000000000000000000000000000000000000..255961a310940f6de2d8c8866c9e2e91023ee7af --- /dev/null +++ b/backend/src/models/image.js @@ -0,0 +1,78 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var ImageSchema = new Schema( + { + auditId: { type: Schema.Types.ObjectId, ref: 'Audit' }, + value: { type: String, required: true, unique: true }, + name: String, + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get one image +ImageSchema.statics.getOne = imageId => { + return new Promise((resolve, reject) => { + var query = Image.findById(imageId); + + query.select('auditId value name'); + query + .exec() + .then(row => { + if (row) resolve(row); + else throw { fn: 'NotFound', message: 'Image not found' }; + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create image +ImageSchema.statics.create = image => { + return new Promise((resolve, reject) => { + var query = Image.findOne({ value: image.value }); + query + .exec() + .then(row => { + if (row) return row; + query = new Image(image); + return query.save(); + }) + .then(row => { + resolve({ _id: row._id }); + }) + .catch(err => { + console.log(err); + reject(err); + }); + }); +}; + +// Delete image +ImageSchema.statics.delete = imageId => { + return new Promise((resolve, reject) => { + var query = Image.findByIdAndDelete(imageId); + query + .exec() + .then(rows => { + if (rows) resolve(rows); + else reject({ fn: 'NotFound', message: 'Image not found' }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var Image = mongoose.model('Image', ImageSchema); +Image.syncIndexes(); +module.exports = Image; diff --git a/backend/src/models/language.js b/backend/src/models/language.js new file mode 100644 index 0000000000000000000000000000000000000000..05f3aaa39dc87230b0bbb1e82886c7029f1e9a8a --- /dev/null +++ b/backend/src/models/language.js @@ -0,0 +1,84 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var LanguageSchema = new Schema( + { + language: { type: String, unique: true }, + locale: { type: String, unique: true }, + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get all languages +LanguageSchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = Language.find(); + query.select('-_id language locale'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create language +LanguageSchema.statics.create = language => { + return new Promise((resolve, reject) => { + var query = new Language(language); + query + .save() + .then(row => { + resolve(row); + }) + .catch(err => { + if (err.code === 11000) + reject({ fn: 'BadParameters', message: 'Language already exists' }); + else reject(err); + }); + }); +}; + +// Update languages +LanguageSchema.statics.updateAll = languages => { + return new Promise((resolve, reject) => { + Language.deleteMany() + .then(row => { + Language.insertMany(languages); + }) + .then(row => { + resolve('Languages updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Delete language +LanguageSchema.statics.delete = locale => { + return new Promise((resolve, reject) => { + Language.deleteOne({ locale: locale }) + .then(res => { + if (res.deletedCount === 1) resolve('Language deleted'); + else reject({ fn: 'NotFound', message: 'Language not found' }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var Language = mongoose.model('Language', LanguageSchema); +module.exports = Language; diff --git a/backend/src/models/settings.js b/backend/src/models/settings.js new file mode 100644 index 0000000000000000000000000000000000000000..8a825a18f2f7ac697de03b0ff67064b9edb8c042 --- /dev/null +++ b/backend/src/models/settings.js @@ -0,0 +1,188 @@ +var mongoose = require('mongoose'); //.set('debug', true); +var Schema = mongoose.Schema; +var _ = require('lodash'); +var Utils = require('../lib/utils.js'); + +// https://stackoverflow.com/questions/25822289/what-is-the-best-way-to-store-color-hex-values-in-mongodb-mongoose +const colorValidator = v => /^#([0-9a-f]{3}){1,2}$/i.test(v); + +const SettingSchema = new Schema( + { + report: { + enabled: { type: Boolean, default: true }, + public: { + cvssColors: { + noneColor: { + type: String, + default: '#4a86e8', + validate: [colorValidator, 'Invalid color'], + }, + lowColor: { + type: String, + default: '#008000', + validate: [colorValidator, 'Invalid color'], + }, + mediumColor: { + type: String, + default: '#f9a009', + validate: [colorValidator, 'Invalid color'], + }, + highColor: { + type: String, + default: '#fe0000', + validate: [colorValidator, 'Invalid color'], + }, + criticalColor: { + type: String, + default: '#212121', + validate: [colorValidator, 'Invalid color'], + }, + }, + captions: { + type: [{ type: String, unique: true }], + default: ['Figure'], + }, + highlightWarning: { type: Boolean, default: false }, + highlightWarningColor: { + type: String, + default: '#ffff25', + validate: [colorValidator, 'Invalid color'], + }, + requiredFields: { + company: { type: Boolean, default: false }, + client: { type: Boolean, default: false }, + dateStart: { type: Boolean, default: false }, + dateEnd: { type: Boolean, default: false }, + dateReport: { type: Boolean, default: false }, + scope: { type: Boolean, default: false }, + findingType: { type: Boolean, default: false }, + findingDescription: { type: Boolean, default: false }, + findingObservation: { type: Boolean, default: false }, + findingReferences: { type: Boolean, default: false }, + findingProofs: { type: Boolean, default: false }, + findingAffected: { type: Boolean, default: false }, + findingRemediationDifficulty: { type: Boolean, default: false }, + findingPriority: { type: Boolean, default: false }, + findingRemediation: { type: Boolean, default: false }, + }, + }, + private: { + imageBorder: { type: Boolean, default: false }, + imageBorderColor: { + type: String, + default: '#000000', + validate: [colorValidator, 'Invalid color'], + }, + }, + }, + reviews: { + enabled: { type: Boolean, default: false }, + public: { + mandatoryReview: { type: Boolean, default: false }, + minReviewers: { + type: Number, + default: 1, + min: 1, + max: 100, + validate: [Number.isInteger, 'Invalid integer'], + }, + }, + private: { + removeApprovalsUponUpdate: { type: Boolean, default: false }, + }, + }, + }, + { strict: true }, +); + +// Get all settings +SettingSchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + const query = Settings.findOne({}); + query.select('-_id -__v'); + query + .exec() + .then(settings => { + resolve(settings); + }) + .catch(err => reject(err)); + }); +}; + +// Get public settings +SettingSchema.statics.getPublic = () => { + return new Promise((resolve, reject) => { + const query = Settings.findOne({}); + query.select( + '-_id report.enabled report.public reviews.enabled reviews.public', + ); + query + .exec() + .then(settings => resolve(settings)) + .catch(err => reject(err)); + }); +}; + +// Update Settings +SettingSchema.statics.update = settings => { + return new Promise((resolve, reject) => { + const query = Settings.findOneAndUpdate({}, settings, { + new: true, + runValidators: true, + }); + query + .exec() + .then(settings => resolve(settings)) + .catch(err => reject(err)); + }); +}; + +// Restore settings to default +SettingSchema.statics.restoreDefaults = () => { + return new Promise((resolve, reject) => { + const query = Settings.deleteMany({}); + query + .exec() + .then(_ => { + const query = new Settings({}); + query + .save() + .then(_ => resolve('Restored default settings.')) + .catch(err => reject(err)); + }) + .catch(err => reject(err)); + }); +}; + +const Settings = mongoose.model('Settings', SettingSchema); + +// Populate/update settings when server starts +Settings.findOne() + .then(liveSettings => { + if (!liveSettings) { + console.log('Initializing Settings'); + Settings.create({}).catch(err => { + throw 'Error creating the settings in the database : ' + err; + }); + } else { + var needUpdate = false; + var liveSettingsPaths = Utils.getObjectPaths(liveSettings.toObject()); + + liveSettingsPaths.forEach(path => { + if (!SettingSchema.path(path) && !path.startsWith('_')) { + needUpdate = true; + _.set(liveSettings, path, undefined); + } + }); + + if (needUpdate) { + console.log('Removing unused fields from Settings'); + liveSettings.save(); + } + } + }) + .catch(err => { + throw 'Error checking for initial settings in the database : ' + err; + }); + +module.exports = Settings; diff --git a/backend/src/models/template.js b/backend/src/models/template.js new file mode 100644 index 0000000000000000000000000000000000000000..423b711c54632bdd8f91ced05fb7e4cf4fac5942 --- /dev/null +++ b/backend/src/models/template.js @@ -0,0 +1,110 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var TemplateSchema = new Schema( + { + name: { type: String, required: true, unique: true }, + ext: { type: String, required: true, unique: false }, + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get all templates +TemplateSchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = Template.find(); + query.select('name ext'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get one template +TemplateSchema.statics.getOne = templateId => { + return new Promise((resolve, reject) => { + var query = Template.findById(templateId); + query.select('name ext'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create template +TemplateSchema.statics.create = template => { + return new Promise((resolve, reject) => { + var query = new Template(template); + query + .save() + .then(row => { + resolve({ _id: row._id, name: row.name, ext: row.ext }); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Template name already exists', + }); + else reject(err); + }); + }); +}; + +// Update template +TemplateSchema.statics.update = (templateId, template) => { + return new Promise((resolve, reject) => { + var query = Template.findByIdAndUpdate(templateId, template); + query + .exec() + .then(rows => { + if (rows) resolve(rows); + else reject({ fn: 'NotFound', message: 'Template not found' }); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Template name already exists', + }); + else reject(err); + }); + }); +}; + +// Delete template +TemplateSchema.statics.delete = templateId => { + return new Promise((resolve, reject) => { + var query = Template.findByIdAndDelete(templateId); + query + .exec() + .then(rows => { + if (rows) resolve(rows); + else reject({ fn: 'NotFound', message: 'Template not found' }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var Template = mongoose.model('Template', TemplateSchema); +module.exports = Template; diff --git a/backend/src/models/user.js b/backend/src/models/user.js new file mode 100644 index 0000000000000000000000000000000000000000..41071d492c1d317b7a0d0fe866e1f424ca906098 --- /dev/null +++ b/backend/src/models/user.js @@ -0,0 +1,430 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var bcrypt = require('bcrypt'); +var jwt = require('jsonwebtoken'); + +var auth = require('../lib/auth.js'); +const { generateUUID } = require('../lib/utils.js'); +var _ = require('lodash'); + +var QRCode = require('qrcode'); +var OTPAuth = require('otpauth'); + +var UserSchema = new Schema( + { + username: { type: String, unique: true, required: true }, + password: { type: String, required: true }, + firstname: { type: String, required: true }, + lastname: { type: String, required: true }, + email: { type: String, required: false }, + phone: { type: String, required: false }, + role: { type: String, default: 'user' }, + totpEnabled: { type: Boolean, default: false }, + totpSecret: { type: String, default: '' }, + enabled: { type: Boolean, default: true }, + refreshTokens: [ + { _id: false, sessionId: String, userAgent: String, token: String }, + ], + }, + { timestamps: true }, +); + +var totpConfig = { + issuer: 'AuditForge', + label: '', + algorithm: 'SHA1', + digits: 6, + period: 30, + secret: '', +}; + +//check TOTP token +var checkTotpToken = function (token, secret) { + if (!token) throw { fn: 'BadParameters', message: 'TOTP token required' }; + if (token.length !== 6) + throw { fn: 'BadParameters', message: 'Invalid TOTP token length' }; + if (!secret) throw { fn: 'BadParameters', message: 'TOTP secret required' }; + + let newConfig = totpConfig; + newConfig.secret = secret; + let totp = new OTPAuth.TOTP(newConfig); + let delta = totp.validate({ + token: token, + window: 5, + }); + //The token is valid in 2 windows in the past and the future, current window is 0. + if (delta === null) { + throw { fn: 'Unauthorized', message: 'Wrong TOTP token.' }; + } else if (delta < -2 || delta > 2) { + throw { fn: 'Unauthorized', message: 'TOTP token out of window.' }; + } + return true; +}; + +/* + *** Statics *** + */ + +// Create user +UserSchema.statics.create = function (user) { + return new Promise((resolve, reject) => { + var hash = bcrypt.hashSync(user.password, 10); + user.password = hash; + new User(user) + .save() + .then(function () { + resolve(); + }) + .catch(function (err) { + if (err.code === 11000) + reject({ fn: 'BadParameters', message: 'Username already exists' }); + else reject(err); + }); + }); +}; + +// Get all users +UserSchema.statics.getAll = function () { + return new Promise((resolve, reject) => { + var query = this.find(); + query.select( + 'username firstname lastname email phone role totpEnabled enabled', + ); + query + .exec() + .then(function (rows) { + resolve(rows); + }) + .catch(function (err) { + reject(err); + }); + }); +}; + +// Get one user by its username +UserSchema.statics.getByUsername = function (username) { + return new Promise((resolve, reject) => { + var query = this.findOne({ username: username }); + query.select( + 'username firstname lastname email phone role totpEnabled enabled', + ); + query + .exec() + .then(function (row) { + if (row) resolve(row); + else throw { fn: 'NotFound', message: 'User not found' }; + }) + .catch(function (err) { + reject(err); + }); + }); +}; + +// Update user with password verification (for updating my profile) +UserSchema.statics.updateProfile = function (username, user) { + return new Promise((resolve, reject) => { + var query = this.findOne({ username: username }); + var payload = {}; + query + .exec() + .then(function (row) { + if (!row) throw { fn: 'NotFound', message: 'User not found' }; + else if (bcrypt.compareSync(user.password, row.password)) { + if (user.username) row.username = user.username; + if (user.firstname) row.firstname = user.firstname; + if (user.lastname) row.lastname = user.lastname; + if (!_.isNil(user.email)) row.email = user.email; + if (!_.isNil(user.phone)) row.phone = user.phone; + if (user.newPassword) + row.password = bcrypt.hashSync(user.newPassword, 10); + if (typeof user.totpEnabled == 'boolean') + row.totpEnabled = user.totpEnabled; + + payload.id = row._id; + payload.username = row.username; + payload.role = row.role; + payload.firstname = row.firstname; + payload.lastname = row.lastname; + payload.email = row.email; + payload.phone = row.phone; + payload.roles = auth.acl.getRoles(payload.role); + + return row.save(); + } else + throw { fn: 'Unauthorized', message: 'Current password is invalid' }; + }) + .then(function () { + var token = jwt.sign(payload, auth.jwtSecret, { + expiresIn: '15 minutes', + }); + resolve({ token: `JWT ${token}` }); + }) + .catch(function (err) { + if (err.code === 11000) + reject({ fn: 'BadParameters', message: 'Username already exists' }); + else reject(err); + }); + }); +}; + +// Update user (for admin usage) +UserSchema.statics.updateUser = function (userId, user) { + return new Promise((resolve, reject) => { + if (user.password) user.password = bcrypt.hashSync(user.password, 10); + var query = this.findOneAndUpdate({ _id: userId }, user); + query + .exec() + .then(function (row) { + if (row) resolve('User updated successfully'); + else reject({ fn: 'NotFound', message: 'User not found' }); + }) + .catch(function (err) { + if (err.code === 11000) + reject({ fn: 'BadParameters', message: 'Username already exists' }); + else reject(err); + }); + }); +}; + +// Update refreshtoken +UserSchema.statics.updateRefreshToken = function (refreshToken, userAgent) { + return new Promise((resolve, reject) => { + var token = ''; + var newRefreshToken = ''; + try { + var decoded = jwt.verify(refreshToken, auth.jwtRefreshSecret); + var userId = decoded.userId; + var sessionId = decoded.sessionId; + var expiration = decoded.exp; + } catch (err) { + if (err.name === 'TokenExpiredError') + throw { fn: 'Unauthorized', message: 'Expired refreshToken' }; + else throw { fn: 'Unauthorized', message: 'Invalid refreshToken' }; + } + var query = this.findById(userId); + query + .exec() + .then(row => { + if (row && row.enabled !== false) { + // Check session exist and sessionId not null (if null then it is a login) + if (sessionId !== null) { + var sessionExist = row.refreshTokens.findIndex( + e => e.sessionId === sessionId && e.token === refreshToken, + ); + if (sessionExist === -1) + // Not found + throw { fn: 'Unauthorized', message: 'Session not found' }; + } + + // Generate new token + var payload = {}; + payload.id = row._id; + payload.username = row.username; + payload.role = row.role; + payload.firstname = row.firstname; + payload.lastname = row.lastname; + payload.email = row.email; + payload.phone = row.phone; + payload.roles = auth.acl.getRoles(payload.role); + + token = jwt.sign(payload, auth.jwtSecret, { + expiresIn: '15 minutes', + }); + + // Remove expired sessions + row.refreshTokens = row.refreshTokens.filter(e => { + try { + var decoded = jwt.verify(e.token, auth.jwtRefreshSecret); + } catch (err) { + var decoded = null; + } + return decoded !== null; + }); + // Update or add new refresh token + var foundIndex = row.refreshTokens.findIndex( + e => e.sessionId === sessionId, + ); + if (foundIndex === -1) { + // Not found + sessionId = generateUUID(); + newRefreshToken = jwt.sign( + { sessionId: sessionId, userId: userId }, + auth.jwtRefreshSecret, + { expiresIn: '7 days' }, + ); + row.refreshTokens.push({ + sessionId: sessionId, + userAgent: userAgent, + token: newRefreshToken, + }); + } else { + newRefreshToken = jwt.sign( + { sessionId: sessionId, userId: userId, exp: expiration }, + auth.jwtRefreshSecret, + ); + row.refreshTokens[foundIndex].token = newRefreshToken; + } + return row.save(); + } else if (row) { + reject({ fn: 'Unauthorized', message: 'Account disabled' }); + } else reject({ fn: 'NotFound', message: 'Session not found' }); + }) + .then(() => { + resolve({ token: token, refreshToken: newRefreshToken }); + }) + .catch(err => { + if (err.code === 11000) + reject({ fn: 'BadParameters', message: 'Username already exists' }); + else reject(err); + }); + }); +}; + +// Remove session +UserSchema.statics.removeSession = function (userId, sessionId) { + return new Promise((resolve, reject) => { + var query = this.findById(userId); + query + .exec() + .then(row => { + if (row) { + row.refreshTokens = row.refreshTokens.filter( + e => e.sessionId !== sessionId, + ); + return row.save(); + } else reject({ fn: 'NotFound', message: 'User not found' }); + }) + .then(() => { + resolve('Session removed successfully'); + }) + .catch(err => { + if (err.code === 11000) + reject({ fn: 'BadParameters', message: 'Username already exists' }); + else reject(err); + }); + }); +}; + +// gen totp QRCode url +UserSchema.statics.getTotpQrcode = function (username) { + return new Promise((resolve, reject) => { + let newConfig = totpConfig; + newConfig.label = username; + const secret = new OTPAuth.Secret({ + size: 10, + }).base32; + newConfig.secret = secret; + + let totp = new OTPAuth.TOTP(newConfig); + let totpUrl = totp.toString(); + + QRCode.toDataURL(totpUrl, function (err, url) { + resolve({ + totpQrCode: url, + totpSecret: secret, + }); + }); + }); +}; + +// verify TOTP and Setup enabled status and secret code +UserSchema.statics.setupTotp = function (token, secret, username) { + return new Promise((resolve, reject) => { + checkTotpToken(token, secret); + + var query = this.findOne({ username: username }); + query + .exec() + .then(function (row) { + if (!row) throw { errmsg: 'User not found' }; + else if (row.totpEnabled === true) + throw { errmsg: 'TOTP already enabled by this user' }; + else { + row.totpEnabled = true; + row.totpSecret = secret; + return row.save(); + } + }) + .then(function () { + resolve({ msg: true }); + }) + .catch(function (err) { + reject(err); + }); + }); +}; + +// verify TOTP and Cancel enabled status and secret code +UserSchema.statics.cancelTotp = function (token, username) { + return new Promise((resolve, reject) => { + var query = this.findOne({ username: username }); + query + .exec() + .then(function (row) { + if (!row) throw { errmsg: 'User not found' }; + else if (row.totpEnabled !== true) + throw { errmsg: 'TOTP is not enabled yet' }; + else { + checkTotpToken(token, row.totpSecret); + row.totpEnabled = false; + row.totpSecret = ''; + return row.save(); + } + }) + .then(function () { + resolve({ msg: 'TOTP is canceled.' }); + }) + .catch(function (err) { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +// Authenticate user with username and password, return JWT token +UserSchema.methods.getToken = function (userAgent) { + return new Promise((resolve, reject) => { + var user = this; + var query = User.findOne({ username: user.username }); + query + .exec() + .then(function (row) { + if (row && row.enabled === false) + throw { fn: 'Unauthorized', message: 'Authentication Failed.' }; + + if (row && bcrypt.compareSync(user.password, row.password)) { + if (row.totpEnabled && user.totpToken) + checkTotpToken(user.totpToken, row.totpSecret); + else if (row.totpEnabled) + throw { fn: 'BadParameters', message: 'Missing TOTP token' }; + var refreshToken = jwt.sign( + { sessionId: null, userId: row._id }, + auth.jwtRefreshSecret, + ); + return User.updateRefreshToken(refreshToken, userAgent); + } else { + if (!row) { + // We compare two random strings to generate delay + var randomHash = + '$2b$10$' + + [...Array(53)].map(() => Math.random().toString(36)[2]).join(''); + bcrypt.compareSync(user.password, randomHash); + } + + throw { fn: 'Unauthorized', message: 'Authentication Failed.' }; + } + }) + .then(row => { + resolve({ token: row.token, refreshToken: row.refreshToken }); + }) + .catch(function (err) { + reject(err); + }); + }); +}; + +var User = mongoose.model('User', UserSchema); +module.exports = User; diff --git a/backend/src/models/vulnerability-category.js b/backend/src/models/vulnerability-category.js new file mode 100644 index 0000000000000000000000000000000000000000..8da2b2e722fab502e073f44330f95e438ff73029 --- /dev/null +++ b/backend/src/models/vulnerability-category.js @@ -0,0 +1,124 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var VulnerabilityCategorySchema = new Schema( + { + name: { type: String, unique: true }, + sortValue: { type: String, default: 'cvssScore' }, + sortOrder: { type: String, enum: ['desc', 'asc'], default: 'desc' }, + sortAuto: { type: Boolean, default: true }, + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get all vulnerabilityCategorys +VulnerabilityCategorySchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = VulnerabilityCategory.find(); + query.select('name sortValue sortOrder sortAuto'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create vulnerabilityCategory +VulnerabilityCategorySchema.statics.create = vulnerabilityCategory => { + return new Promise((resolve, reject) => { + var query = new VulnerabilityCategory(vulnerabilityCategory); + query + .save() + .then(row => { + resolve(row); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Vulnerability Category name already exists', + }); + else reject(err); + }); + }); +}; + +// Update vulnerabilityCategory +VulnerabilityCategorySchema.statics.update = (name, vulnerabilityCategory) => { + return new Promise((resolve, reject) => { + var query = VulnerabilityCategory.findOneAndUpdate( + { name: name }, + vulnerabilityCategory, + ); + query + .exec() + .then(row => { + if (row) resolve(row); + else + reject({ + fn: 'NotFound', + message: 'Vulnerability category not found', + }); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Vulnerability Category already exists', + }); + else reject(err); + }); + }); +}; + +// Update vulnerability Categories +VulnerabilityCategorySchema.statics.updateAll = vulnCategories => { + return new Promise((resolve, reject) => { + VulnerabilityCategory.deleteMany() + .then(row => { + VulnerabilityCategory.insertMany(vulnCategories); + }) + .then(row => { + resolve('Vulnerability Categories updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Delete vulnerabilityCategory +VulnerabilityCategorySchema.statics.delete = name => { + return new Promise((resolve, reject) => { + VulnerabilityCategory.deleteOne({ name: name }) + .then(res => { + if (res.deletedCount === 1) resolve('Vulnerability Category deleted'); + else + reject({ + fn: 'NotFound', + message: 'Vulnerability Category not found', + }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var VulnerabilityCategory = mongoose.model( + 'VulnerabilityCategory', + VulnerabilityCategorySchema, +); +module.exports = VulnerabilityCategory; diff --git a/backend/src/models/vulnerability-type.js b/backend/src/models/vulnerability-type.js new file mode 100644 index 0000000000000000000000000000000000000000..cb0b07b7e215a765c86ff4c9e00a626bac268504 --- /dev/null +++ b/backend/src/models/vulnerability-type.js @@ -0,0 +1,96 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var VulnerabilityTypeSchema = new Schema( + { + name: String, + locale: String, + }, + { timestamps: true }, +); + +VulnerabilityTypeSchema.index( + { name: 1, locale: 1 }, + { name: 'unique_name_locale', unique: true }, +); + +/* + *** Statics *** + */ + +// Get all vulnerabilityTypes +VulnerabilityTypeSchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = VulnerabilityType.find(); + query.select('-_id name locale'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create vulnerabilityType +VulnerabilityTypeSchema.statics.create = vulnerabilityType => { + return new Promise((resolve, reject) => { + var query = new VulnerabilityType(vulnerabilityType); + query + .save() + .then(row => { + resolve(row); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Vulnerability Type already exists', + }); + else reject(err); + }); + }); +}; + +// Update vulnerability Types +VulnerabilityTypeSchema.statics.updateAll = vulnerabilityTypes => { + return new Promise((resolve, reject) => { + VulnerabilityType.deleteMany() + .then(row => { + VulnerabilityType.insertMany(vulnerabilityTypes); + }) + .then(row => { + resolve('Vulnerability Types updated successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Delete vulnerabilityType +VulnerabilityTypeSchema.statics.delete = name => { + return new Promise((resolve, reject) => { + VulnerabilityType.deleteOne({ name: name }) + .then(res => { + if (res.deletedCount === 1) resolve('Vulnerability Type deleted'); + else + reject({ fn: 'NotFound', message: 'Vulnerability Type not found' }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var VulnerabilityType = mongoose.model( + 'VulnerabilityType', + VulnerabilityTypeSchema, +); +module.exports = VulnerabilityType; diff --git a/backend/src/models/vulnerability-update.js b/backend/src/models/vulnerability-update.js new file mode 100644 index 0000000000000000000000000000000000000000..987e0c31d55defc87f30dcfea53383ea83547103 --- /dev/null +++ b/backend/src/models/vulnerability-update.js @@ -0,0 +1,190 @@ +var mongoose = require('mongoose'); +var _ = require('lodash'); + +var Schema = mongoose.Schema; + +var customField = { + _id: false, + customField: { type: Schema.Types.ObjectId, ref: 'CustomField' }, + text: Schema.Types.Mixed, +}; + +var VulnerabilityUpdateSchema = new Schema( + { + vulnerability: { + type: Schema.Types.ObjectId, + ref: 'Vulnerability', + required: true, + }, + creator: { type: Schema.Types.ObjectId, ref: 'User', required: true }, + cvssv3: String, + priority: { type: Number, enum: [1, 2, 3, 4] }, + remediationComplexity: { type: Number, enum: [1, 2, 3] }, + references: [String], + locale: String, + title: String, + vulnType: String, + description: String, + observation: String, + remediation: String, + category: String, + customFields: [customField], + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get all vulnerabilities +VulnerabilityUpdateSchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = VulnerabilityUpdate.find(); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get all updates of vulnerability +VulnerabilityUpdateSchema.statics.getAllByVuln = vulnId => { + return new Promise((resolve, reject) => { + var query = VulnerabilityUpdate.find({ vulnerability: vulnId }); + query.populate('creator', '-_id username'); + query.populate('customFields.customField', 'fieldType label'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create vulnerability +VulnerabilityUpdateSchema.statics.create = (username, vulnerability) => { + return new Promise((resolve, reject) => { + var created = true; + var User = mongoose.model('User'); + var creator = ''; + var Vulnerability = mongoose.model('Vulnerability'); + var query = User.findOne({ username: username }); + query + .exec() + .then(row => { + if (row) { + creator = row._id; + var query = Vulnerability.findOne({ + 'details.title': vulnerability.title, + }); + return query.exec(); + } else throw { fn: 'NotFound', message: 'User not found' }; + }) + .then(row => { + if (row) { + if (row.status === 1) + throw { + fn: 'Forbidden', + message: 'Vulnerability not approved yet', + }; + else { + // Check if there are any changes from the original vulnerability + var detail = row.details.find( + d => d.locale === vulnerability.locale, + ); + // console.log(vulnerability.customFields) + // console.log(detail.customFields) + if ( + typeof detail !== 'undefined' && + (row.cvssv3 || '').includes(vulnerability.cvssv3) && + vulnerability.priority === (row.priority || null) && + vulnerability.remediationComplexity === + (row.remediationComplexity || null) && + _.isEqual(vulnerability.references, detail.references || []) && + vulnerability.category === (row.category || null) && + vulnerability.vulnType === (detail.vulnType || null) && + vulnerability.description === (detail.description || null) && + vulnerability.observation === (detail.observation || null) && + vulnerability.remediation === (detail.remediation || null) && + vulnerability.customFields.length === + detail.customFields.length && + vulnerability.customFields.every((e, idx) => { + return ( + e.customField._id == detail.customFields[idx].customField && + e.text === detail.customFields[idx].text + ); + }) + ) { + throw { + fn: 'BadParameters', + message: 'No changes from the original vulnerability', + }; + } + vulnerability.vulnerability = row._id; + vulnerability.creator = creator; + var query = new VulnerabilityUpdate(vulnerability); + created = false; + return query.save(); + } + } else { + var vuln = {}; + vuln.cvssv3 = vulnerability.cvssv3 || null; + vuln.priority = vulnerability.priority || null; + vuln.remediationComplexity = + vulnerability.remediationComplexity || null; + vuln.category = vulnerability.category || null; + vuln.creator = creator; + var details = {}; + details.locale = vulnerability.locale || null; + details.title = vulnerability.title || null; + details.vulnType = vulnerability.vulnType || null; + details.description = vulnerability.description || null; + details.observation = vulnerability.observation || null; + details.remediation = vulnerability.remediation || null; + details.references = vulnerability.references || null; + details.customFields = vulnerability.customFields || []; + vuln.details = [details]; + var query = new Vulnerability(vuln); + return query.save(); + } + }) + .then(row => { + if (created) resolve('Finding created as new Vulnerability'); + else { + var query = Vulnerability.findOneAndUpdate( + { 'details.title': vulnerability.title }, + { status: 2 }, + ); + return query.exec(); + } + }) + .then(row => { + resolve('Update proposed for existing vulnerability'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +VulnerabilityUpdateSchema.statics.deleteAllByVuln = async vulnId => { + return await VulnerabilityUpdate.deleteMany({ vulnerability: vulnId }); +}; + +/* + *** Methods *** + */ + +var VulnerabilityUpdate = mongoose.model( + 'VulnerabilityUpdate', + VulnerabilityUpdateSchema, +); +module.exports = VulnerabilityUpdate; diff --git a/backend/src/models/vulnerability.js b/backend/src/models/vulnerability.js new file mode 100644 index 0000000000000000000000000000000000000000..9d0cd00778d80b8f2ca48f9762e01c55fc913849 --- /dev/null +++ b/backend/src/models/vulnerability.js @@ -0,0 +1,272 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var customField = { + _id: false, + customField: { type: Schema.Types.ObjectId, ref: 'CustomField' }, + text: Schema.Types.Mixed, +}; + +var VulnerabilityDetails = { + _id: false, + locale: String, + // language: String, + title: { type: String, unique: true, sparse: true }, + vulnType: String, + description: String, + observation: String, + remediation: String, + cwes: [String], + references: [String], + customFields: [customField], +}; + +var VulnerabilitySchema = new Schema( + { + cvssv3: String, + priority: { type: Number, enum: [1, 2, 3, 4] }, + remediationComplexity: { type: Number, enum: [1, 2, 3] }, + details: [VulnerabilityDetails], + status: { type: Number, enum: [0, 1, 2], default: 1 }, // 0: validated, 1: created, 2: updated, + category: String, + creator: { type: Schema.Types.ObjectId, ref: 'User' }, + }, + { timestamps: true }, +); + +/* + *** Statics *** + */ + +// Get all vulnerabilities +VulnerabilitySchema.statics.getAll = () => { + return new Promise((resolve, reject) => { + var query = Vulnerability.find(); + query.populate('creator', '-_id username'); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get all vulnerabilities for download +VulnerabilitySchema.statics.export = () => { + return new Promise((resolve, reject) => { + var query = Vulnerability.find(); + query.select( + 'details cvssv3 priority remediationComplexity cwes references category -_id', + ); + query + .exec() + .then(rows => { + resolve(rows); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Create vulnerability +VulnerabilitySchema.statics.create = vulnerabilities => { + return new Promise((resolve, reject) => { + Vulnerability.insertMany(vulnerabilities, { ordered: false }) + .then(rows => { + resolve({ created: rows.length, duplicates: 0 }); + }) + .catch(err => { + if (err.code === 11000) { + if (err.result.nInserted === 0) + reject({ + fn: 'BadParameters', + message: 'Vulnerability title already exists', + }); + else { + var errorMessages = []; + err.writeErrors.forEach(e => + errorMessages.push(e.errmsg || 'no errmsg'), + ); + resolve({ + created: err.result.nInserted, + duplicates: errorMessages, + }); + } + } else reject(err); + }); + }); +}; + +// Update vulnerability +VulnerabilitySchema.statics.update = (vulnerabilityId, vulnerability) => { + return new Promise((resolve, reject) => { + var VulnerabilityUpdate = mongoose.model('VulnerabilityUpdate'); + var query = Vulnerability.findByIdAndUpdate(vulnerabilityId, vulnerability); + query + .exec() + .then(row => { + if (!row) + reject({ fn: 'NotFound', message: 'Vulnerability not found' }); + else { + var query = VulnerabilityUpdate.deleteMany({ + vulnerability: vulnerabilityId, + }); + return query.exec(); + } + }) + .then(row => { + resolve('Vulnerability updated successfully'); + }) + .catch(err => { + if (err.code === 11000) + reject({ + fn: 'BadParameters', + message: 'Vulnerability title already exists', + }); + else reject(err); + }); + }); +}; + +// Delete all vulnerabilities +VulnerabilitySchema.statics.deleteAll = () => { + return new Promise((resolve, reject) => { + var query = Vulnerability.deleteMany(); + query + .exec() + .then(() => { + resolve('All vulnerabilities deleted successfully'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Delete vulnerability +VulnerabilitySchema.statics.delete = vulnerabilityId => { + return new Promise((resolve, reject) => { + var query = Vulnerability.findByIdAndDelete(vulnerabilityId); + query + .exec() + .then(rows => { + if (rows) resolve(rows); + else reject({ fn: 'NotFound', message: 'Vulnerability not found' }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +// Get vulnerabilities by language +VulnerabilitySchema.statics.getAllByLanguage = locale => { + return new Promise((resolve, reject) => { + var query = Vulnerability.find({ 'details.locale': locale }); + query.select('details cvssv3 priority remediationComplexity category'); + query + .exec() + .then(rows => { + if (rows) { + var result = []; + rows.forEach(row => { + row.details.forEach(detail => { + if (detail.locale === locale && detail.title) { + var temp = {}; + temp.cvssv3 = row.cvssv3; + temp.priority = row.priority; + temp.remediationComplexity = row.remediationComplexity; + temp.category = row.category; + temp.detail = detail; + temp._id = row._id; + result.push(temp); + } + }); + }); + resolve(result); + } else + reject({ + fn: 'NotFound', + message: 'Locale with existing title not found', + }); + }) + .catch(err => { + reject(err); + }); + }); +}; + +VulnerabilitySchema.statics.Merge = (vulnIdPrime, vulnIdMerge, locale) => { + return new Promise((resolve, reject) => { + var mergeDetail = null; + var mergeVuln = null; + var primeVuln = null; + var query = Vulnerability.findById(vulnIdMerge); + query + .exec() + .then(row => { + if (!row) + reject({ fn: 'NotFound', message: 'Vulnerability not found' }); + else { + mergeVuln = row; + mergeDetail = row.details.find(d => d.locale === locale); + var query = Vulnerability.findById(vulnIdPrime); + return query.exec(); + } + }) + .then(row => { + if (!row) + reject({ fn: 'NotFound', message: 'Vulnerability not found' }); + else { + if (row.details.findIndex(d => d.locale === locale && d.title) !== -1) + reject({ + fn: 'BadParameters', + message: 'Language already exists in this vulnerability', + }); + else { + primeVuln = row; + var removeIndex = mergeVuln.details + .map(d => d.title) + .indexOf(mergeDetail.title); + mergeVuln.details.splice(removeIndex, 1); + if (mergeVuln.details.length === 0) + return Vulnerability.findByIdAndDelete(mergeVuln._id); + else return mergeVuln.save(); + } + } + }) + .then(() => { + var detail = {}; + detail.locale = mergeDetail.locale; + detail.title = mergeDetail.title; + if (mergeDetail.vulnType) detail.vulnType = mergeDetail.vulnType; + if (mergeDetail.description) + detail.description = mergeDetail.description; + if (mergeDetail.observation) + detail.observation = mergeDetail.observation; + if (mergeDetail.remediation) + detail.remediation = mergeDetail.remediation; + if (mergeDetail.customFields) + detail.customFields = mergeDetail.customFields; + primeVuln.details.push(detail); + return primeVuln.save(); + }) + .then(() => { + resolve('Vulnerability merge successfull'); + }) + .catch(err => { + reject(err); + }); + }); +}; + +/* + *** Methods *** + */ + +var Vulnerability = mongoose.model('Vulnerability', VulnerabilitySchema); +module.exports = Vulnerability; diff --git a/backend/src/routes/audit.js b/backend/src/routes/audit.js new file mode 100644 index 0000000000000000000000000000000000000000..626a34e319545ec0a09e38b79e252f32d69f8785 --- /dev/null +++ b/backend/src/routes/audit.js @@ -0,0 +1,1168 @@ +module.exports = function (app, io) { + var Response = require('../lib/httpResponse'); + var Audit = require('mongoose').model('Audit'); + var acl = require('../lib/auth').acl; + var reportGenerator = require('../lib/report-generator'); + var _ = require('lodash'); + var utils = require('../lib/utils'); + var Settings = require('mongoose').model('Settings'); + + /* ### AUDITS LIST ### */ + + // Get audits list of user (all for admin) with regex filter on findings + app.get('/api/audits', acl.hasPermission('audits:read'), function (req, res) { + var getUsersRoom = function (room) { + return utils.getSockets(io, room).map(s => s.username); + }; + var filters = {}; + if (req.query.findingTitle) + filters['findings.title'] = new RegExp( + utils.escapeRegex(req.query.findingTitle), + 'i', + ); + if (req.query.type && req.query.type === 'default') + filters.$or = [{ type: 'default' }, { type: { $exists: false } }]; + if (req.query.type && ['multi', 'retest'].includes(req.query.type)) + filters.type = req.query.type; + + Audit.getAudits( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.decodedToken.id, + filters, + ) + .then(msg => { + var result = []; + msg.forEach(audit => { + var a = {}; + a._id = audit._id; + a.name = audit.name; + a.language = audit.language; + a.auditType = audit.auditType; + a.creator = audit.creator; + a.collaborators = audit.collaborators; + a.company = audit.company; + a.createdAt = audit.createdAt; + a.ext = + !!audit.template && !!audit.template.ext + ? audit.template.ext + : 'Template error'; + a.reviewers = audit.reviewers; + a.approvals = audit.approvals; + a.state = audit.state; + a.type = audit.type; + a.parentId = audit.parentId; + if (acl.isAllowed(req.decodedToken.role, 'audits:users-connected')) { + a.connected = getUsersRoom(audit._id.toString()); + } + result.push(a); + }); + Response.Ok(res, result); + }) + .catch(err => Response.Internal(res, err)); + }); + + // Create audit (default or multi) with name, auditType, language provided + // parentId can be set only if type is default + app.post( + '/api/audits', + acl.hasPermission('audits:create'), + function (req, res) { + if (!req.body.name || !req.body.language || !req.body.auditType) { + Response.BadParameters( + res, + 'Missing some required parameters: name, language, auditType', + ); + return; + } + + if (!utils.validFilename(req.body.language)) { + Response.BadParameters(res, 'Invalid characters for language'); + return; + } + + var audit = {}; + // Required params + audit.name = req.body.name; + audit.language = req.body.language; + audit.auditType = req.body.auditType; + audit.type = 'default'; + + // Optional params + if (req.body.type && req.body.type === 'multi') + audit.type = req.body.type; + if (audit.type === 'default' && req.body.parentId) + audit.parentId = req.body.parentId; + + Audit.create(audit, req.decodedToken.id) + .then(inserted => + Response.Created(res, { + message: 'Audit created successfully', + audit: inserted, + }), + ) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get audits children + app.get( + '/api/audits/:auditId/children', + acl.hasPermission('audits:read'), + function (req, res) { + var getUsersRoom = function (room) { + return utils.getSockets(io, room).map(s => s.username); + }; + Audit.getAuditChildren( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(msg => { + var result = []; + msg.forEach(audit => { + var a = {}; + a._id = audit._id; + a.name = audit.name; + a.auditType = audit.auditType; + a.approvals = audit.approvals; + a.state = audit.state; + if ( + acl.isAllowed(req.decodedToken.role, 'audits:users-connected') + ) { + a.connected = getUsersRoom(audit._id.toString()); + } + result.push(a); + }); + Response.Ok(res, result); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get audit retest with auditId + app.get( + '/api/audits/:auditId/retest', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getRetest( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create audit retest with auditId + app.post( + '/api/audits/:auditId/retest', + acl.hasPermission('audits:create'), + function (req, res) { + if (!req.body.auditType) { + Response.BadParameters( + res, + 'Missing some required parameters: auditType', + ); + return; + } + Audit.createRetest( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + req.body.auditType, + ) + .then(inserted => + Response.Created(res, { + message: 'Audit Retest created successfully', + audit: inserted, + }), + ) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete audit if creator or admin + app.delete( + '/api/audits/:auditId', + acl.hasPermission('audits:delete'), + function (req, res) { + Audit.delete( + acl.isAllowed(req.decodedToken.role, 'audits:delete-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + /* ### AUDITS EDIT ### */ + + // Get Audit with ID + app.get( + '/api/audits/:auditId', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get audit general information + app.get( + '/api/audits/:auditId/general', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getGeneral( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update audit general information + app.put( + '/api/audits/:auditId/general', + acl.hasPermission('audits:update'), + async function (req, res) { + var update = {}; + + var settings = await Settings.getAll(); + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ); + if (settings.reviews.enabled && audit.state !== 'EDIT') { + Response.Forbidden( + res, + 'The audit is not in the EDIT state and therefore cannot be edited.', + ); + return; + } + + if (req.body.reviewers) { + if (req.body.reviewers.some(element => !element._id)) { + Response.BadParameters(res, 'One or more reviewer is missing an _id'); + return; + } + + // Is the new reviewer the creator of the audit? + if ( + req.body.reviewers.some(element => element._id === audit.creator._id) + ) { + Response.BadParameters( + res, + 'A user cannot simultaneously be a reviewer and a collaborator/creator', + ); + return; + } + + // Is the new reviewer one of the new collaborators that will override current collaborators? + if (req.body.collaborators) { + req.body.reviewers.forEach(reviewer => { + if ( + req.body.collaborators.some( + element => !element._id || element._id === reviewer._id, + ) + ) { + Response.BadParameters( + res, + 'A user cannot simultaneously be a reviewer and a collaborator/creator', + ); + return; + } + }); + } + + // If no new collaborators are being set, is the new reviewer one of the current collaborators? + else if (audit.collaborators) { + req.body.reviewers.forEach(reviewer => { + if ( + audit.collaborators.some(element => element._id === reviewer._id) + ) { + Response.BadParameters( + res, + 'A user cannot simultaneously be a reviewer and a collaborator/creator', + ); + return; + } + }); + } + } + + if (req.body.collaborators) { + if (req.body.collaborators.some(element => !element._id)) { + Response.BadParameters( + res, + 'One or more collaborator is missing an _id', + ); + return; + } + + // Are the new collaborators part of the current reviewers? + req.body.collaborators.forEach(collaborator => { + if ( + audit.reviewers.some(element => element._id === collaborator._id) + ) { + Response.BadParameters( + res, + 'A user cannot simultaneously be a reviewer and a collaborator/creator', + ); + return; + } + }); + + // If the new collaborator already gave a review, remove said review, accept collaborator + if (audit.approvals) { + var newApprovals = audit.approvals.filter( + approval => + !req.body.collaborators.some( + collaborator => approval.toString() === collaborator._id, + ), + ); + update.approvals = newApprovals; + } + } + + // Optional parameters + if (req.body.name) update.name = req.body.name; + if (req.body.date) update.date = req.body.date; + if (req.body.date_start) update.date_start = req.body.date_start; + if (req.body.date_end) update.date_end = req.body.date_end; + if (req.body.client !== undefined) update.client = req.body.client; + if (req.body.company !== undefined) { + update.company = {}; + if (req.body.company && req.body.company._id) + update.company._id = req.body.company._id; + else if (req.body.company && req.body.company.name) + update.company.name = req.body.company.name; + else update.company = null; + } + if (req.body.collaborators) update.collaborators = req.body.collaborators; + if (req.body.reviewers) update.reviewers = req.body.reviewers; + if (req.body.language && utils.validFilename(req.body.language)) + update.language = req.body.language; + if (req.body.scope && typeof (req.body.scope === 'array')) { + update.scope = req.body.scope.map(item => { + return { name: item }; + }); + } + if (req.body.template) update.template = req.body.template; + if (req.body.customFields) update.customFields = req.body.customFields; + if ( + settings.reviews.enabled && + settings.reviews.private.removeApprovalsUponUpdate + ) + update.approvals = []; + + Audit.updateGeneral( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + update, + ) + .then(msg => { + io.to(req.params.auditId).emit('updateAudit'); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get audit network information + app.get( + '/api/audits/:auditId/network', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getNetwork( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update audit network information + app.put( + '/api/audits/:auditId/network', + acl.hasPermission('audits:update'), + async function (req, res) { + var settings = await Settings.getAll(); + + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ); + if (settings.reviews.enabled && audit.state !== 'EDIT') { + Response.Forbidden( + res, + 'The audit is not in the EDIT state and therefore cannot be edited.', + ); + return; + } + + var update = {}; + // Optional parameters + if (req.body.scope) update.scope = req.body.scope; + if ( + settings.reviews.enabled && + settings.reviews.private.removeApprovalsUponUpdate + ) + update.approvals = []; + + Audit.updateNetwork( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + update, + ) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // POST to export an encrypted PDF. + app.post( + '/api/audits/:auditId/generate/pdf', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(async audit => { + if ( + ['ppt', 'pptx', 'doc', 'docx', 'docm'].includes(audit.template.ext) + ) { + let reportPdf; + if (req.body.password) { + reportPdf = await reportGenerator.generateEncryptedPdf( + audit, + req.body.password, + ); + } else { + Response.BadParameters(res, 'No password included'); + } + + if (reportPdf) { + res.setHeader( + 'Content-Disposition', + `attachment; filename=${audit.name}.pdf`, + ); + res.setHeader('Content-Type', 'application/pdf'); + res.send(reportPdf); + } else { + console.error('Error generating PDF'); + Response.Internal(res, 'Error generating PDF'); + } + } else { + Response.BadParameters( + res, + 'Template not in a Microsoft Word/Powerpoint format', + ); + } + }) + .catch(err => { + console.log(err); + if (err.code === 'ENOENT') + Response.BadParameters(res, 'Template File not found'); + else Response.Internal(res, err); + }); + }, + ); + + // Generate report as PDF + app.get( + '/api/audits/:auditId/generate/pdf', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(async audit => { + if ( + ['ppt', 'pptx', 'doc', 'docx', 'docm'].find( + ext => ext === audit.template.ext, + ) + ) { + var reportPdf = await reportGenerator.generatePdf(audit); + Response.SendFile(res, `${audit.name}.pdf`, reportPdf); + } else { + Response.BadParameters( + res, + 'Template not in a Microsoft Word/Powerpoint format', + ); + } + }) + .catch(err => { + console.log(err); + if (err.code === 'ENOENT') + Response.BadParameters(res, 'Template File not found'); + else Response.Internal(res, err); + }); + }, + ); + + // Generate Report as csv + app.get( + '/api/audits/:auditId/generate/csv', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(async audit => { + var reportCsv = await reportGenerator.generateCsv(audit); + Response.SendFile(res, `${audit.name}.csv`, reportCsv); + }) + .catch(err => { + console.log(err); + Response.Internal(res, err); + }); + }, + ); + + // Generate Report as json + app.get( + '/api/audits/:auditId/generate/json', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(async audit => { + Response.SendFile(res, `${audit.name}.json`, audit); + }) + .catch(err => { + console.log(err); + Response.Internal(res, err); + }); + }, + ); + + // Add finding to audit + app.post( + '/api/audits/:auditId/findings', + acl.hasPermission('audits:update'), + async function (req, res) { + var settings = await Settings.getAll(); + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ); + if (settings.reviews.enabled && audit.state !== 'EDIT') { + Response.Forbidden( + res, + 'The audit is not in the EDIT state and therefore cannot be edited.', + ); + return; + } + if (!req.body.title) { + Response.BadParameters(res, 'Missing some required parameters: title'); + return; + } + + var finding = {}; + // Required parameters + finding.title = req.body.title; + + // Optional parameters + if (req.body.vulnType) finding.vulnType = req.body.vulnType; + if (req.body.description) finding.description = req.body.description; + if (req.body.observation) finding.observation = req.body.observation; + if (req.body.remediation) finding.remediation = req.body.remediation; + if (req.body.remediationComplexity) + finding.remediationComplexity = req.body.remediationComplexity; + if (req.body.priority) finding.priority = req.body.priority; + if (req.body.references) finding.references = req.body.references; + if (req.body.cwes) finding.cwes = req.body.cwes; + if (req.body.cvssv3) finding.cvssv3 = req.body.cvssv3; + if (req.body.poc) finding.poc = req.body.poc; + if (req.body.scope) finding.scope = req.body.scope; + if (req.body.status !== undefined) finding.status = req.body.status; + if (req.body.category) finding.category = req.body.category; + if (req.body.customFields) finding.customFields = req.body.customFields; + + if ( + settings.reviews.enabled && + settings.reviews.private.removeApprovalsUponUpdate + ) { + Audit.updateGeneral( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + { approvals: [] }, + ); + } + + Audit.createFinding( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + finding, + ) + .then(msg => { + io.to(req.params.auditId).emit('updateAudit'); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get finding of audit + app.get( + '/api/audits/:auditId/findings/:findingId', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getFinding( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + req.params.findingId, + ) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update finding of audit + app.put( + '/api/audits/:auditId/findings/:findingId', + acl.hasPermission('audits:update'), + async function (req, res) { + var settings = await Settings.getAll(); + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ); + if (settings.reviews.enabled && audit.state !== 'EDIT') { + Response.Forbidden( + res, + 'The audit is not in the EDIT state and therefore cannot be edited.', + ); + return; + } + + var finding = {}; + // Optional parameters + if (req.body.title) finding.title = req.body.title; + if (req.body.vulnType) finding.vulnType = req.body.vulnType; + if (!_.isNil(req.body.description)) + finding.description = req.body.description; + if (!_.isNil(req.body.observation)) + finding.observation = req.body.observation; + if (!_.isNil(req.body.remediation)) + finding.remediation = req.body.remediation; + if (req.body.remediationComplexity) + finding.remediationComplexity = req.body.remediationComplexity; + if (req.body.priority) finding.priority = req.body.priority; + if (req.body.references) finding.references = req.body.references; + if (req.body.cwes) finding.cwes = req.body.cwes; + if (req.body.cvssv3) finding.cvssv3 = req.body.cvssv3; + if (!_.isNil(req.body.poc)) finding.poc = req.body.poc; + if (!_.isNil(req.body.scope)) finding.scope = req.body.scope; + if (req.body.status !== undefined) finding.status = req.body.status; + if (req.body.category) finding.category = req.body.category; + if (req.body.customFields) finding.customFields = req.body.customFields; + if (req.body.retestDescription) + finding.retestDescription = req.body.retestDescription; + if (req.body.retestStatus) finding.retestStatus = req.body.retestStatus; + + if ( + settings.reviews.enabled && + settings.reviews.private.removeApprovalsUponUpdate + ) { + Audit.updateGeneral( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + { approvals: [] }, + ); + } + + Audit.updateFinding( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + req.params.findingId, + finding, + ) + .then(msg => { + io.to(req.params.auditId).emit('updateAudit'); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete finding of audit + app.delete( + '/api/audits/:auditId/findings/:findingId', + acl.hasPermission('audits:update'), + async function (req, res) { + var settings = await Settings.getAll(); + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ); + if (settings.reviews.enabled && audit.state !== 'EDIT') { + Response.Forbidden( + res, + 'The audit is not in the EDIT state and therefore cannot be edited.', + ); + return; + } + Audit.deleteFinding( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + req.params.findingId, + ) + .then(msg => { + io.to(req.params.auditId).emit('updateAudit'); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get section of audit + app.get( + '/api/audits/:auditId/sections/:sectionId', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getSection( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + req.params.sectionId, + ) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update section of audit + app.put( + '/api/audits/:auditId/sections/:sectionId', + acl.hasPermission('audits:update'), + async function (req, res) { + var settings = await Settings.getAll(); + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ); + if (settings.reviews.enabled && audit.state !== 'EDIT') { + Response.Forbidden( + res, + 'The audit is not in the EDIT state and therefore cannot be edited.', + ); + return; + } + if (typeof req.body.customFields === 'undefined') { + Response.BadParameters( + res, + 'Missing some required parameters: customFields', + ); + return; + } + var section = {}; + // Mandatory parameters + section.customFields = req.body.customFields; + + // For retrocompatibility with old section.text usage + if (req.body.text) section.text = req.body.text; + + if ( + settings.reviews.enabled && + settings.reviews.private.removeApprovalsUponUpdate + ) { + Audit.updateGeneral( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + { approvals: [] }, + ); + } + + Audit.updateSection( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + req.params.sectionId, + section, + ) + .then(msg => { + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Generate Report for specific audit + app.get( + '/api/audits/:auditId/generate', + acl.hasPermission('audits:read'), + function (req, res) { + Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(async audit => { + var settings = await Settings.getAll(); + + if ( + settings.reviews.enabled && + settings.reviews.public.mandatoryReview && + audit.state !== 'APPROVED' + ) { + Response.Forbidden( + res, + 'Audit was not approved therefore cannot be exported.', + ); + return; + } + + if (!audit.template) + throw { fn: 'BadParameters', message: 'Template not defined' }; + + var reportDoc = await reportGenerator.generateDoc(audit); + Response.SendFile( + res, + `${audit.name.replace(/[\\\/:*?"<>|]/g, '')}.${audit.template.ext || 'docx'}`, + reportDoc, + ); + }) + .catch(err => { + if (err.code === 'ENOENT') + Response.BadParameters(res, 'Template File not found'); + else Response.Internal(res, err); + }); + }, + ); + + // Update sort options of an audit + app.put( + '/api/audits/:auditId/sortfindings', + acl.hasPermission('audits:update'), + async function (req, res) { + var settings = await Settings.getAll(); + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ); + if (settings.reviews.enabled && audit.state !== 'EDIT') { + Response.Forbidden( + res, + 'The audit is not in the EDIT state and therefore cannot be edited.', + ); + return; + } + var update = {}; + // Optional parameters + if (req.body.sortFindings) update.sortFindings = req.body.sortFindings; + if ( + settings.reviews.enabled && + settings.reviews.private.removeApprovalsUponUpdate + ) + update.approvals = []; + + Audit.updateSortFindings( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + update, + ) + .then(msg => { + io.to(req.params.auditId).emit('updateAudit'); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update finding position (oldIndex -> newIndex) + app.put( + '/api/audits/:auditId/movefinding', + acl.hasPermission('audits:update'), + async function (req, res) { + var settings = await Settings.getAll(); + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ); + if (settings.reviews.enabled && audit.state !== 'EDIT') { + Response.Forbidden( + res, + 'The audit is not in the EDIT state and therefore cannot be edited.', + ); + return; + } + if ( + typeof req.body.oldIndex === 'undefined' || + typeof req.body.newIndex === 'undefined' + ) { + Response.BadParameters( + res, + 'Missing some required parameters: oldIndex, newIndex', + ); + return; + } + + var move = {}; + // Required parameters + move.oldIndex = req.body.oldIndex; + move.newIndex = req.body.newIndex; + + if ( + settings.reviews.enabled && + settings.reviews.private.removeApprovalsUponUpdate + ) { + Audit.updateGeneral( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + { approvals: [] }, + ); + } + + Audit.moveFindingPosition( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + move, + ) + .then(msg => { + io.to(req.params.auditId).emit('updateAudit'); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Give or remove a reviewer's approval to an audit + app.put( + '/api/audits/:auditId/toggleApproval', + acl.hasPermission('audits:review'), + async function (req, res) { + const settings = await Settings.getAll(); + + if (!settings.reviews.enabled) { + Response.Forbidden(res, 'Audit reviews are not enabled.'); + return; + } + + Audit.findById(req.params.auditId) + .then(audit => { + if (audit.state !== 'REVIEW' && audit.state !== 'APPROVED') { + Response.Forbidden( + res, + 'The audit is not approvable in the current state.', + ); + return; + } + + var hasApprovedBefore = false; + var newApprovalsArray = []; + if (audit.approvals) { + audit.approvals.forEach(approval => { + if (approval._id.toString() === req.decodedToken.id) { + hasApprovedBefore = true; + } else { + newApprovalsArray.push(approval); + } + }); + } + + if (!hasApprovedBefore) { + newApprovalsArray.push({ + _id: req.decodedToken.id, + role: req.decodedToken.role, + username: req.decodedToken.username, + firstname: req.decodedToken.firstname, + lastname: req.decodedToken.lastname, + }); + } + + var update = { approvals: newApprovalsArray }; + Audit.updateApprovals( + acl.isAllowed(req.decodedToken.role, 'audits:review-all'), + req.params.auditId, + req.decodedToken.id, + update, + ) + .then(() => { + io.to(req.params.auditId).emit('updateAudit'); + Response.Ok(res, 'Approval updated successfully.'); + }) + .catch(err => { + Response.Internal(res, err); + }); + }) + .catch(err => { + Response.Internal(res, err); + }); + }, + ); + + // Sets the audit state to EDIT or REVIEW + app.put( + '/api/audits/:auditId/updateReadyForReview', + acl.hasPermission('audits:update'), + async function (req, res) { + const settings = await Settings.getAll(); + + if (!settings.reviews.enabled) { + Response.Forbidden(res, 'Audit reviews are not enabled.'); + return; + } + + var update = {}; + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ); + + if (audit.state !== 'EDIT' && audit.state !== 'REVIEW') { + Response.Forbidden( + res, + 'The audit is not in the proper state for this action.', + ); + return; + } + + if ( + req.body.state != undefined && + (req.body.state === 'EDIT' || req.body.state === 'REVIEW') + ) + update.state = req.body.state; + + if (update.state === 'EDIT') { + var newApprovalsArray = []; + if (audit.approvals) { + audit.approvals.forEach(approval => { + if (approval._id.toString() !== req.decodedToken.id) { + newApprovalsArray.push(approval); + } + }); + update.approvals = newApprovalsArray; + } + } + + Audit.updateGeneral( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + update, + ) + .then(msg => { + io.to(req.params.auditId).emit('updateAudit'); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update parentId of Audit + app.put( + '/api/audits/:auditId/updateParent', + acl.hasPermission('audits:create'), + async function (req, res) { + var settings = await Settings.getAll(); + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.body.parentId, + req.decodedToken.id, + ); + if (settings.reviews.enabled && audit.state !== 'EDIT') { + Response.Forbidden( + res, + 'The audit is not in the EDIT state and therefore cannot be edited.', + ); + return; + } + if (!req.body.parentId) { + Response.BadParameters( + res, + 'Missing some required parameters: parentId', + ); + return; + } + Audit.updateParent( + acl.isAllowed(req.decodedToken.role, 'audits:update-all'), + req.params.auditId, + req.decodedToken.id, + req.body.parentId, + ) + .then(msg => { + io.to(req.body.parentId).emit('updateAudit'); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete parentId of Audit + app.delete( + '/api/audits/:auditId/deleteParent', + acl.hasPermission('audits:delete'), + async function (req, res) { + var settings = await Settings.getAll(); + var audit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + req.params.auditId, + req.decodedToken.id, + ); + var parentAudit = await Audit.getAudit( + acl.isAllowed(req.decodedToken.role, 'audits:read-all'), + audit.parentId, + req.decodedToken.id, + ); + if (settings.reviews.enabled && parentAudit.state !== 'EDIT') { + Response.Forbidden( + res, + 'The audit is not in the EDIT state and therefore cannot be edited.', + ); + return; + } + Audit.deleteParent( + acl.isAllowed(req.decodedToken.role, 'audits:delete-all'), + req.params.auditId, + req.decodedToken.id, + ) + .then(msg => { + if (msg.parentId) io.to(msg.parentId.toString()).emit('updateAudit'); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); +}; diff --git a/backend/src/routes/check-cwe-update.js b/backend/src/routes/check-cwe-update.js new file mode 100644 index 0000000000000000000000000000000000000000..a579de5ea82c9f2398d7c25f9a5bdc98f8cf0a95 --- /dev/null +++ b/backend/src/routes/check-cwe-update.js @@ -0,0 +1,60 @@ +module.exports = function (app) { + const Response = require('../lib/httpResponse.js'); + const acl = require('../lib/auth').acl; + const networkError = new Error( + 'Error checking CWE model update: Network response was not ok', + ); + const timeoutError = new Error( + 'Error checking CWE mode update: Request timed out', + ); + const cweConfig = require('../config/config-cwe.json')['cwe-container']; + const TIMEOUT_MS = cweConfig.check_timeout_ms || 30000; + + app.get( + '/api/check-cwe-update', + acl.hasPermission('check-update:all'), + async function (req, res) { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS); + + try { + //TODO: Change workaround to a proper solution for self-signed certificates + if (!cweConfig.host || !cweConfig.port) { + return Response.BadRequest( + res, + new Error('Configuración del servicio incompleta'), + ); + } + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + const response = await fetch( + `https://${cweConfig.host}:${cweConfig.port}/${cweConfig.endpoints.check_update_endpoint}`, + { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + signal: controller.signal, + }, + ); + clearTimeout(timeout); + + if (!response.ok) { + const errorBody = await response.text(); + throw new Error( + `Error del servidor CWE (${response.status}): ${errorBody}`, + ); + } + + const data = await response.json(); + res.json(data); + } catch (error) { + console.error('Error en check-cwe-update:', { + name: error.name, + message: error.message, + stack: error.stack, + }); + error.name === 'AbortError' + ? Response.Internal(res, { ...timeoutError, details: error.message }) + : Response.Internal(res, { ...networkError, details: error.message }); + } + }, + ); +}; diff --git a/backend/src/routes/client.js b/backend/src/routes/client.js new file mode 100644 index 0000000000000000000000000000000000000000..bc124589f54f1c3b9b2208e5546ce9e318709761 --- /dev/null +++ b/backend/src/routes/client.js @@ -0,0 +1,80 @@ +module.exports = function (app) { + var Response = require('../lib/httpResponse.js'); + var Client = require('mongoose').model('Client'); + var acl = require('../lib/auth').acl; + + // Get clients list + app.get( + '/api/clients', + acl.hasPermission('clients:read'), + function (req, res) { + Client.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create client + app.post( + '/api/clients', + acl.hasPermission('clients:create'), + function (req, res) { + if (!req.body.email) { + Response.BadParameters(res, 'Required parameters: email'); + return; + } + + var client = {}; + // Required parameters + client.email = req.body.email; + + // Optional parameters + if (req.body.lastname) client.lastname = req.body.lastname; + if (req.body.firstname) client.firstname = req.body.firstname; + if (req.body.phone) client.phone = req.body.phone; + if (req.body.cell) client.cell = req.body.cell; + if (req.body.title) client.title = req.body.title; + var company = null; + if (req.body.company && req.body.company.name) + company = req.body.company.name; + + Client.create(client, company) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update client + app.put( + '/api/clients/:id', + acl.hasPermission('clients:update'), + function (req, res) { + var client = {}; + // Optional parameters + if (req.body.email) client.email = req.body.email; + client.lastname = req.body.lastname || null; + client.firstname = req.body.firstname || null; + client.phone = req.body.phone || null; + client.cell = req.body.cell || null; + client.title = req.body.title || null; + var company = null; + if (req.body.company && req.body.company.name) + company = req.body.company.name; + + Client.update(req.params.id, client, company) + .then(msg => Response.Ok(res, 'Client updated successfully')) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete client + app.delete( + '/api/clients/:id', + acl.hasPermission('clients:delete'), + function (req, res) { + Client.delete(req.params.id) + .then(msg => Response.Ok(res, 'Client deleted successfully')) + .catch(err => Response.Internal(res, err)); + }, + ); +}; diff --git a/backend/src/routes/company.js b/backend/src/routes/company.js new file mode 100644 index 0000000000000000000000000000000000000000..f16083ec45b28d41ffcbc1d62b45900cdcb6420e --- /dev/null +++ b/backend/src/routes/company.js @@ -0,0 +1,68 @@ +module.exports = function (app) { + var Response = require('../lib/httpResponse.js'); + var Company = require('mongoose').model('Company'); + var acl = require('../lib/auth').acl; + + // Get companies list + app.get( + '/api/companies', + acl.hasPermission('companies:read'), + function (req, res) { + Company.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create company + app.post( + '/api/companies', + acl.hasPermission('companies:create'), + function (req, res) { + if (!req.body.name) { + Response.BadParameters(res, 'Required paramters: name'); + return; + } + + var company = {}; + // Required parameters + company.name = req.body.name; + + // Optional parameters + if (req.body.shortName) company.shortName = req.body.shortName; + if (req.body.logo) company.logo = req.body.logo; + + Company.create(company) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update company + app.put( + '/api/companies/:id', + acl.hasPermission('companies:update'), + function (req, res) { + var company = {}; + // Optional parameters + if (req.body.name) company.name = req.body.name; + if (req.body.shortName) company.shortName = req.body.shortName; + if (req.body.logo) company.logo = req.body.logo; + + Company.update(req.params.id, company) + .then(msg => Response.Ok(res, 'Company updated successfully')) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete company + app.delete( + '/api/companies/:id', + acl.hasPermission('companies:delete'), + function (req, res) { + Company.delete(req.params.id) + .then(msg => Response.Ok(res, 'Company deleted successfully')) + .catch(err => Response.Internal(res, err)); + }, + ); +}; diff --git a/backend/src/routes/cvss.js b/backend/src/routes/cvss.js new file mode 100644 index 0000000000000000000000000000000000000000..ff03444b0cc5c42dcce0524eab89b8c32fe7c817 --- /dev/null +++ b/backend/src/routes/cvss.js @@ -0,0 +1,61 @@ +module.exports = function (app) { + const Response = require('../lib/httpResponse.js'); + const acl = require('../lib/auth').acl; + const cweConfig = require('../config/config-cwe.json')['cwe-container']; + const errorClassify = new Error('Error classifying vulnerability'); + const networkError = new Error('Network response was not ok'); + const timeoutError = new Error('Request timed out'); + const TIMEOUT_MS = 47000; // 47 segundos (temporal) + + // Get CVSS string from description + app.post( + '/api/cvss', + acl.hasPermission('classify_cvss:all'), + async function (req, res) { + if ( + !req.body.vuln || + typeof req.body.vuln !== 'string' || + req.body.vuln.trim() === '' + ) { + Response.BadParameters(res, 'Required parameters: description'); + return; + } + + const vuln = { + vuln: req.body.vuln.trim(), + }; + + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS); + + try { + //TODO: Change workaround to a proper solution for self-signed certificates + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + const response = await fetch( + `https://${cweConfig.host}:${cweConfig.port}/cvss`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(vuln), + signal: controller.signal, + }, + ); + + if (!response.ok) { + throw networkError; + } + + const data = await response.json(); + res.json(data); + } catch (error) { + console.error(error); + error.name === 'AbortError' + ? Response.Internal(res, timeoutError) + : Response.Internal(res, errorClassify); + } finally { + clearTimeout(timeout); + } + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; + }, + ); +}; diff --git a/backend/src/routes/cwe.js b/backend/src/routes/cwe.js new file mode 100644 index 0000000000000000000000000000000000000000..1180914b0396236206565d6b8d1e023a83fb119b --- /dev/null +++ b/backend/src/routes/cwe.js @@ -0,0 +1,60 @@ +module.exports = function (app) { + const Response = require('../lib/httpResponse.js'); + const acl = require('../lib/auth').acl; + const cweConfig = require('../config/config-cwe.json')['cwe-container']; + const errorClassify = new Error('Error classifying vulnerability'); + const networkError = new Error('Network response was not ok'); + const timeoutError = new Error('Request timed out'); + const TIMEOUT_MS = 5000; // 5 segundos + + // Get CWE classification from description + app.post( + '/api/classify', + acl.hasPermission('classify:all'), + async function (req, res) { + if ( + !req.body.vuln || + typeof req.body.vuln !== 'string' || + req.body.vuln.trim() === '' + ) { + Response.BadParameters(res, 'Required parameters: description'); + return; + } + + const vuln = { + vuln: req.body.vuln.trim(), + }; + + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS); + + try { + //TODO: Change workaround to a proper solution for self-signed certificates + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + const response = await fetch( + `https://${cweConfig.host}:${cweConfig.port}/classify`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(vuln), + signal: controller.signal, + }, + ); + clearTimeout(timeout); + + if (!response.ok) { + throw networkError; + } + + const data = await response.json(); + res.json(data); + } catch (error) { + console.error(error); + error.name === 'AbortError' + ? Response.Internal(res, timeoutError) + : Response.Internal(res, errorClassify); + } + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; + }, + ); +}; diff --git a/backend/src/routes/data.js b/backend/src/routes/data.js new file mode 100644 index 0000000000000000000000000000000000000000..712184fb4e629c00105b1bbe86ef19877ac72fff --- /dev/null +++ b/backend/src/routes/data.js @@ -0,0 +1,666 @@ +const { isArray } = require('lodash'); + +module.exports = function (app) { + var Response = require('../lib/httpResponse.js'); + var acl = require('../lib/auth').acl; + var utils = require('../lib/utils'); + var Language = require('mongoose').model('Language'); + var AuditType = require('mongoose').model('AuditType'); + var VulnerabilityType = require('mongoose').model('VulnerabilityType'); + var VulnerabilityCategory = require('mongoose').model( + 'VulnerabilityCategory', + ); + var CustomSection = require('mongoose').model('CustomSection'); + var CustomField = require('mongoose').model('CustomField'); + + var _ = require('lodash'); + + /* ===== ROLES ===== */ + + // Get Roles list + app.get( + '/api/data/roles', + acl.hasPermission('roles:read'), + function (req, res) { + try { + var result = Object.keys(acl.roles); + Response.Ok(res, result); + } catch (error) { + Response.Internal(res, error); + } + }, + ); + + /* ===== LANGUAGES ===== */ + + // Get languages list + app.get( + '/api/data/languages', + acl.hasPermission('languages:read'), + function (req, res) { + Language.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create language + app.post( + '/api/data/languages', + acl.hasPermission('languages:create'), + function (req, res) { + if (!req.body.locale || !req.body.language) { + Response.BadParameters( + res, + 'Missing required parameters: locale, language', + ); + return; + } + if ( + !utils.validFilename(req.body.language) || + !utils.validFilename(req.body.locale) + ) { + Response.BadParameters( + res, + "language and locale value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + + var language = {}; + language.locale = req.body.locale; + language.language = req.body.language; + + Language.create(language) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete Language + app.delete( + '/api/data/languages/:locale(*)', + acl.hasPermission('languages:delete'), + function (req, res) { + Language.delete(req.params.locale) + .then(msg => { + Response.Ok(res, 'Language deleted successfully'); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update Languages + app.put( + '/api/data/languages', + acl.hasPermission('languages:update'), + function (req, res) { + for (var i = 0; i < req.body.length; i++) { + var language = req.body[i]; + if (!language.locale || !language.language) { + Response.BadParameters( + res, + 'Missing required parameters: locale, language', + ); + return; + } + if ( + !utils.validFilename(language.language) || + !utils.validFilename(language.locale) + ) { + Response.BadParameters( + res, + "language and locale value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + } + + var languages = []; + req.body.forEach(e => { + languages.push({ language: e.language, locale: e.locale }); + }); + + Language.updateAll(languages) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + /* ===== AUDIT TYPES ===== */ + + // Get audit types list + app.get( + '/api/data/audit-types', + acl.hasPermission('audit-types:read'), + function (req, res) { + AuditType.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create audit type + app.post( + '/api/data/audit-types', + acl.hasPermission('audit-types:create'), + function (req, res) { + if (!req.body.name || !req.body.templates) { + Response.BadParameters( + res, + 'Missing required parameters: name, templates', + ); + return; + } + if (!utils.validFilename(req.body.name)) { + Response.BadParameters( + res, + "name and locale value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + + var auditType = {}; + auditType.stage = 'default'; + // Required parameters + auditType.name = req.body.name; + auditType.templates = req.body.templates; + + // Optional parameters + if (req.body.sections) auditType.sections = req.body.sections; + if (req.body.hidden) auditType.hidden = req.body.hidden; + if ( + req.body.stage && + (req.body.stage === 'multi' || req.body.stage === 'retest') + ) + auditType.stage = req.body.stage; + + // Fix hidden sections for multi and retest audits + if (auditType.stage === 'multi' || auditType.stage === 'retest') + auditType.hidden = ['network']; + + AuditType.create(auditType) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete audit type + app.delete( + '/api/data/audit-types/:name(*)', + acl.hasPermission('audit-types:delete'), + function (req, res) { + AuditType.delete(req.params.name) + .then(msg => { + Response.Ok(res, 'Audit type deleted successfully'); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update Audit Types + app.put( + '/api/data/audit-types', + acl.hasPermission('audit-types:update'), + function (req, res) { + for (var i = 0; i < req.body.length; i++) { + var auditType = req.body[i]; + if (!auditType.name || !auditType.templates) { + Response.BadParameters( + res, + 'Missing required parameters: name, templates', + ); + return; + } + if (!utils.validFilename(auditType.name)) { + Response.BadParameters( + res, + "name and locale value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + } + + var auditTypes = []; + req.body.forEach(e => { + // Fix hidden sections for multi and retest audits + if (e.stage === 'multi' || e.stage === 'retest') + auditTypes.push({ + name: e.name, + templates: e.templates, + sections: e.sections, + hidden: ['network'], + stage: e.stage, + }); + else + auditTypes.push({ + name: e.name, + templates: e.templates, + sections: e.sections, + hidden: e.hidden, + stage: 'default', + }); + }); + + AuditType.updateAll(auditTypes) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + /* ===== VULNERABILITY TYPES ===== */ + + // Get vulnerability types list + app.get( + '/api/data/vulnerability-types', + acl.hasPermission('vulnerability-types:read'), + function (req, res) { + VulnerabilityType.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create vulnerability type + app.post( + '/api/data/vulnerability-types', + acl.hasPermission('vulnerability-types:create'), + function (req, res) { + if (!req.body.name || !req.body.locale) { + Response.BadParameters( + res, + 'Missing required parameters: name, locale', + ); + return; + } + if ( + !utils.validFilename(req.body.name) || + !utils.validFilename(req.body.locale) + ) { + Response.BadParameters( + res, + "name and locale value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + + var vulnType = {}; + vulnType.name = req.body.name; + vulnType.locale = req.body.locale; + VulnerabilityType.create(vulnType) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete vulnerability type + app.delete( + '/api/data/vulnerability-types/:name(*)', + acl.hasPermission('vulnerability-types:delete'), + function (req, res) { + VulnerabilityType.delete(req.params.name) + .then(msg => { + Response.Ok(res, 'Vulnerability type deleted successfully'); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update Vulnerability Types + app.put( + '/api/data/vulnerability-types', + acl.hasPermission('vulnerability-types:update'), + function (req, res) { + for (var i = 0; i < req.body.length; i++) { + var vulnType = req.body[i]; + if (!vulnType.name || !vulnType.locale) { + Response.BadParameters( + res, + 'Missing required parameters: name, locale', + ); + return; + } + if ( + !utils.validFilename(vulnType.name) || + !utils.validFilename(vulnType.locale) + ) { + Response.BadParameters( + res, + "name and locale value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + } + + var vulnTypes = []; + req.body.forEach(e => { + vulnTypes.push({ name: e.name, locale: e.locale }); + }); + + VulnerabilityType.updateAll(vulnTypes) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + /* ===== VULNERABILITY CATEGORY ===== */ + + // Get vulnerability category list + app.get( + '/api/data/vulnerability-categories', + acl.hasPermission('vulnerability-categories:read'), + function (req, res) { + VulnerabilityCategory.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create vulnerability category + app.post( + '/api/data/vulnerability-categories', + acl.hasPermission('vulnerability-categories:create'), + function (req, res) { + if (!req.body.name) { + Response.BadParameters(res, 'Missing required parameters: name'); + return; + } + if (!utils.validFilename(req.body.name)) { + Response.BadParameters( + res, + "name value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + + var vulnCat = {}; + // Required parameters + vulnCat.name = req.body.name; + + // Optional parameters + if (!_.isNil(req.body.sortValue)) vulnCat.sortValue = req.body.sortValue; + if (!_.isNil(req.body.sortOrder)) vulnCat.sortOrder = req.body.sortOrder; + if (!_.isNil(req.body.sortAuto)) vulnCat.sortAuto = req.body.sortAuto; + + VulnerabilityCategory.create(vulnCat) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update Vulnerability Category + app.put( + '/api/data/vulnerability-categories', + acl.hasPermission('vulnerability-categories:update'), + function (req, res) { + for (var i = 0; i < req.body.length; i++) { + var vulnCat = req.body[i]; + if (!vulnCat.name) { + Response.BadParameters(res, 'Missing required parameters: name'); + return; + } + if (!utils.validFilename(vulnCat.name)) { + Response.BadParameters( + res, + "name value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + } + + var vulnCategories = []; + req.body.forEach(e => { + // Required parameters + var tmpCat = { name: e.name }; + + // Optional parameters + if (!_.isNil(e.sortValue)) tmpCat.sortValue = e.sortValue; + if (!_.isNil(e.sortOrder)) tmpCat.sortOrder = e.sortOrder; + if (!_.isNil(e.sortAuto)) tmpCat.sortAuto = e.sortAuto; + + vulnCategories.push(tmpCat); + }); + + VulnerabilityCategory.updateAll(vulnCategories) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete vulnerability category + app.delete( + '/api/data/vulnerability-categories/:name(*)', + acl.hasPermission('vulnerability-categories:delete'), + function (req, res) { + VulnerabilityCategory.delete(req.params.name) + .then(msg => { + Response.Ok(res, 'Vulnerability category deleted successfully'); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + /* ===== SECTIONS ===== */ + + // Get sections list + app.get( + '/api/data/sections', + acl.hasPermission('sections:read'), + function (req, res) { + CustomSection.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create section + app.post( + '/api/data/sections', + acl.hasPermission('sections:create'), + function (req, res) { + if (!req.body.field || !req.body.name) { + Response.BadParameters(res, 'Missing required parameters: field, name'); + return; + } + if ( + !utils.validFilename(req.body.field) || + !utils.validFilename(req.body.name) + ) { + Response.BadParameters( + res, + "name and field value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + + var section = {}; + section.field = req.body.field; + section.name = req.body.name; + section.locale = req.body.locale; + // Optional parameters + if (req.body.text) section.text = req.body.text; + if (req.body.icon) section.icon = req.body.icon; + + CustomSection.create(section) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete section + app.delete( + '/api/data/sections/:field/:locale(*)', + acl.hasPermission('sections:delete'), + function (req, res) { + CustomSection.delete(req.params.field, req.params.locale) + .then(msg => { + Response.Ok(res, 'Section deleted successfully'); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update sections + app.put( + '/api/data/sections', + acl.hasPermission('sections:update'), + function (req, res) { + for (var i = 0; i < req.body.length; i++) { + var section = req.body[i]; + if (!section.name || !section.field) { + Response.BadParameters( + res, + 'Missing required parameters: name, field', + ); + return; + } + if ( + !utils.validFilename(section.name) || + !utils.validFilename(section.field) + ) { + Response.BadParameters( + res, + "name and field value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + } + + var sections = []; + req.body.forEach(e => { + sections.push({ name: e.name, field: e.field, icon: e.icon || '' }); + }); + + CustomSection.updateAll(sections) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + /* ===== CUSTOM FIELDS ===== */ + + // Get custom fields + app.get( + '/api/data/custom-fields', + acl.hasPermission('custom-fields:read'), + function (req, res) { + CustomField.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create custom field + app.post( + '/api/data/custom-fields', + acl.hasPermission('custom-fields:create'), + function (req, res) { + if ( + (!req.body.fieldType || !req.body.label || !req.body.display) && + req.body.fieldType !== 'space' + ) { + Response.BadParameters( + res, + 'Missing required parameters: fieldType, label, display', + ); + return; + } + if ( + (!utils.validFilename(req.body.fieldType) || + !utils.validFilename(req.body.label)) && + req.body.fieldType !== 'space' + ) { + Response.BadParameters( + res, + "name and field value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + + var customField = {}; + customField.fieldType = req.body.fieldType; + customField.label = req.body.label; + customField.display = req.body.display; + if (req.body.displaySub) customField.displaySub = req.body.displaySub; + if (req.body.size) customField.size = req.body.size; + if (req.body.offset) customField.offset = req.body.offset; + if ( + typeof req.body.required === 'boolean' && + req.body.fieldType !== 'space' + ) + customField.required = req.body.required; + if (req.body.description) customField.description = req.body.description; + if (req.body.text) customField.text = req.body.text; + if (req.body.options) customField.options = req.body.options; + if (typeof req.body.position === 'number') + customField.position = req.body.position; + + CustomField.create(customField) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update custom fields + app.put( + '/api/data/custom-fields', + acl.hasPermission('custom-fields:update'), + function (req, res) { + for (var i = 0; i < req.body.length; i++) { + var customField = req.body[i]; + if ( + (!customField.label || !customField._id || !customField.display) && + customField.fieldType !== 'space' + ) { + Response.BadParameters( + res, + 'Missing required parameters: _id, label, display', + ); + return; + } + if ( + !utils.validFilename( + customField.label || !utils.validFilename(customField.fieldType), + ) && + customField.fieldType !== 'space' + ) { + Response.BadParameters( + res, + "label and fieldType value must match /^[p{Letter}p{Mark}0-9 []'()_-]+$/iu", + ); + return; + } + } + + var customFields = []; + req.body.forEach(e => { + var field = { _id: e._id, label: e.label, display: e.display }; + if (typeof e.size === 'number') field.size = e.size; + if (typeof e.offset === 'number') field.offset = e.offset; + if (typeof e.required === 'boolean') field.required = e.required; + if (!_.isNil(e.description)) field.description = e.description; + if (!_.isNil(e.text)) field.text = e.text; + if (isArray(e.options)) field.options = e.options; + if (typeof e.position === 'number') field.position = e.position; + customFields.push(field); + }); + + CustomField.updateAll(customFields) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete custom field + app.delete( + '/api/data/custom-fields/:fieldId', + acl.hasPermission('custom-fields:delete'), + function (req, res) { + CustomField.delete(req.params.fieldId) + .then(msg => { + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); +}; diff --git a/backend/src/routes/image.js b/backend/src/routes/image.js new file mode 100644 index 0000000000000000000000000000000000000000..3b28b23d54dd059568fd5aff2e0f5c3d56a95820 --- /dev/null +++ b/backend/src/routes/image.js @@ -0,0 +1,78 @@ +module.exports = function (app) { + var Response = require('../lib/httpResponse.js'); + var Image = require('mongoose').model('Image'); + var acl = require('../lib/auth').acl; + + // Get image + app.get( + '/api/images/:imageId', + acl.hasPermission('images:read'), + function (req, res) { + Image.getOne(req.params.imageId) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create image + app.post( + '/api/images/', + acl.hasPermission('images:create'), + function (req, res) { + if (!req.body.value) { + Response.BadParameters(res, 'Missing required parameters: value'); + return; + } + + // Type validation + if (typeof req.body.value !== 'string') { + Response.BadParameters(res, 'value parameter must be a String'); + return; + } + + var image = {}; + // Required parameters + image.value = req.body.value; + + // Optional parameters + if (req.body.name) image.name = req.body.name; + if (req.body.auditId) image.auditId = req.body.auditId; + + Image.create(image) + .then(data => Response.Created(res, data)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete image + app.delete( + '/api/images/:imageId', + acl.hasPermission('images:delete'), + function (req, res) { + Image.delete(req.params.imageId) + .then(data => { + Response.Ok(res, 'Image deleted successfully'); + }) + .catch(err => { + Response.Internal(res, err); + }); + }, + ); + + // Download image file + app.get( + '/api/images/download/:imageId', + acl.hasPermission('images:read'), + function (req, res) { + Image.getOne(req.params.imageId) + .then(data => { + var imgBase64 = data.value.split(',')[1]; + var img = Buffer.from(imgBase64, 'base64'); + Response.SendImage(res, img); + }) + .catch(err => { + Response.Internal(res, err); + }); + }, + ); +}; diff --git a/backend/src/routes/settings.js b/backend/src/routes/settings.js new file mode 100644 index 0000000000000000000000000000000000000000..145293487d929a5c2e95a9d61795150d5278327f --- /dev/null +++ b/backend/src/routes/settings.js @@ -0,0 +1,55 @@ +module.exports = function (app) { + var Response = require('../lib/httpResponse.js'); + var acl = require('../lib/auth').acl; + var Settings = require('mongoose').model('Settings'); + + app.get( + '/api/settings', + acl.hasPermission('settings:read'), + function (req, res) { + Settings.getAll() + .then(settings => Response.Ok(res, settings)) + .catch(err => Response.Internal(res, err)); + }, + ); + + app.get( + '/api/settings/public', + acl.hasPermission('settings:read-public'), + function (req, res) { + Settings.getPublic() + .then(settings => Response.Ok(res, settings)) + .catch(err => Response.Internal(res, err)); + }, + ); + + app.put( + '/api/settings', + acl.hasPermission('settings:update'), + function (req, res) { + Settings.update(req.body) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + app.put( + '/api/settings/revert', + acl.hasPermission('settings:update'), + function (req, res) { + Settings.restoreDefaults() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + app.get( + '/api/settings/export', + acl.hasPermission('settings:read'), + function (req, res) { + Settings.getAll() + .then(settings => Response.SendFile(res, 'app-settings.json', settings)) + .catch(err => Response.Internal(res, err)); + }, + ); +}; diff --git a/backend/src/routes/template.js b/backend/src/routes/template.js new file mode 100644 index 0000000000000000000000000000000000000000..4d103c1df8bd789e5a5c08788c60834aa5dd2680 --- /dev/null +++ b/backend/src/routes/template.js @@ -0,0 +1,154 @@ +module.exports = function (app) { + var Response = require('../lib/httpResponse.js'); + var Template = require('mongoose').model('Template'); + var acl = require('../lib/auth').acl; + var utils = require('../lib/utils'); + var fs = require('fs'); + + // Get templates list + app.get( + '/api/templates', + acl.hasPermission('templates:read'), + function (req, res) { + Template.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create template + app.post( + '/api/templates', + acl.hasPermission('templates:create'), + function (req, res) { + if (!req.body.name || !req.body.file || !req.body.ext) { + Response.BadParameters( + res, + 'Missing required parameters: name, ext, file', + ); + return; + } + + if ( + !utils.validFilename(req.body.name) || + !utils.validFilename(req.body.ext) + ) { + Response.BadParameters(res, 'Bad name or ext format'); + return; + } + + var template = {}; + // Required parameters + template.name = req.body.name; + template.ext = req.body.ext; + + Template.create(template) + .then(data => { + var fileBuffer = Buffer.from(req.body.file, 'base64'); + fs.writeFileSync( + `${__basedir}/../report-templates/${template.name}.${template.ext}`, + fileBuffer, + ); + Response.Created(res, data); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update template + app.put( + '/api/templates/:templateId', + acl.hasPermission('templates:update'), + function (req, res) { + if (req.body.name && !utils.validFilename(req.body.name)) { + Response.BadParameters(res, 'Bad name format'); + return; + } + + var template = {}; + // Optional parameters + if (req.body.name) template.name = req.body.name; + if (req.body.file && req.body.ext) template.ext = req.body.ext; + + Template.update(req.params.templateId, template) + .then(data => { + // Update file only + if (!req.body.name && req.body.file && req.body.ext) { + var fileBuffer = Buffer.from(req.body.file, 'base64'); + try { + fs.unlinkSync( + `${__basedir}/../report-templates/${data.name}.${data.ext || 'docx'}`, + ); + } catch {} + fs.writeFileSync( + `${__basedir}/../report-templates/${data.name}.${req.body.ext}`, + fileBuffer, + ); + } + // Update name only + else if (req.body.name && !req.body.file) { + fs.renameSync( + `${__basedir}/../report-templates/${data.name}.${data.ext || 'docx'}`, + `${__basedir}/../report-templates/${req.body.name}.${data.ext || 'docx'}`, + ); + } + // Update both name and file + else if (req.body.name && req.body.file && req.body.ext) { + var fileBuffer = Buffer.from(req.body.file, 'base64'); + try { + fs.unlinkSync( + `${__basedir}/../report-templates/${data.name}.${data.ext || 'docx'}`, + ); + } catch {} + fs.writeFileSync( + `${__basedir}/../report-templates/${req.body.name}.${req.body.ext}`, + fileBuffer, + ); + } + Response.Ok(res, 'Template updated successfully'); + }) + .catch(err => { + if (err.code && err.code === 'ENOENT') + Response.NotFound(res, 'Template File was not Found'); + else Response.Internal(res, err); + }); + }, + ); + + // Delete template + app.delete( + '/api/templates/:templateId', + acl.hasPermission('templates:delete'), + function (req, res) { + Template.delete(req.params.templateId) + .then(data => { + fs.unlinkSync( + `${__basedir}/../report-templates/${data.name}.${data.ext || 'docx'}`, + ); + Response.Ok(res, 'Template deleted successfully'); + }) + .catch(err => { + if (err.code && err.code === 'ENOENT') + Response.Ok( + res, + 'Template File not found but deleted successfully in database', + ); + else Response.Internal(res, err); + }); + }, + ); + + // Download template file + app.get( + '/api/templates/download/:templateId', + acl.hasPermission('templates:read'), + function (req, res) { + Template.getOne(req.params.templateId) + .then(data => { + var file = `${__basedir}/../report-templates/${data.name}.${data.ext || 'docx'}`; + res.download(file, `${data.name}.${data.ext}`); + }) + .catch(err => Response.Internal(res, err)); + }, + ); +}; diff --git a/backend/src/routes/update-cwe-model.js b/backend/src/routes/update-cwe-model.js new file mode 100644 index 0000000000000000000000000000000000000000..267834e09484b0baa25003c35546064899bd4d40 --- /dev/null +++ b/backend/src/routes/update-cwe-model.js @@ -0,0 +1,51 @@ +module.exports = function (app) { + const Response = require('../lib/httpResponse.js'); + const acl = require('../lib/auth').acl; + const networkError = new Error( + 'Error updating CWE model: Network response was not ok', + ); + const timeoutError = new Error('Error updating CWE model: Request timed out'); + const cweConfig = require('../config/config-cwe.json')['cwe-container']; + const TIMEOUT_MS = cweConfig.update_timeout_ms || 120000; + + app.post( + '/api/update-cwe-model', + acl.hasPermission('update-model:all'), + async function (req, res) { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS); + + try { + //TODO: Change workaround to a proper solution for self-signed certificates + if (!cweConfig.host || !cweConfig.port) { + return Response.BadRequest( + res, + new Error('Configuración del servicio CWE incompleta'), + ); + } + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + const response = await fetch( + `https://${cweConfig.host}:${cweConfig.port}/${cweConfig.endpoints.update_cwe_endpoint}`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + signal: controller.signal, + }, + ); + clearTimeout(timeout); + + if (!response.ok) { + throw networkError; + } + + const data = await response.json(); + res.json(data); + } catch (error) { + console.error(error); + error.name === 'AbortError' + ? Response.Internal(res, timeoutError) + : Response.Internal(res, networkError); + } + }, + ); +}; diff --git a/backend/src/routes/user.js b/backend/src/routes/user.js new file mode 100644 index 0000000000000000000000000000000000000000..db59bbb67e473cf4b704c0c4ab875bf2a9c5cb7d --- /dev/null +++ b/backend/src/routes/user.js @@ -0,0 +1,403 @@ +module.exports = function (app) { + var Response = require('../lib/httpResponse.js'); + var User = require('mongoose').model('User'); + var acl = require('../lib/auth').acl; + var jwtRefreshSecret = require('../lib/auth').jwtRefreshSecret; + var jwt = require('jsonwebtoken'); + var _ = require('lodash'); + var passwordpolicy = require('../lib/passwordpolicy'); + + // Check token validity + app.get( + '/api/users/checktoken', + acl.hasPermission('validtoken'), + function (req, res) { + Response.Ok(res, req.cookies['token']); + }, + ); + + // Refresh token + app.get('/api/users/refreshtoken', function (req, res) { + var userAgent = req.headers['user-agent']; + var token = req.cookies['refreshToken']; + + User.updateRefreshToken(token, userAgent) + .then(msg => { + res.cookie('token', `JWT ${msg.token}`, { + secure: true, + httpOnly: true, + }); + res.cookie('refreshToken', msg.refreshToken, { + secure: true, + httpOnly: true, + path: '/api/users/refreshtoken', + }); + Response.Ok(res, msg); + }) + .catch(err => { + if (err.fn === 'Unauthorized') { + res.clearCookie('token'); + res.clearCookie('refreshToken'); + } + Response.Internal(res, err); + }); + }); + + // Remove token cookie + app.delete('/api/users/refreshtoken', function (req, res) { + var token = req.cookies['refreshToken']; + try { + var decoded = jwt.verify(token, jwtRefreshSecret); + } catch (err) { + res.clearCookie('token'); + res.clearCookie('refreshToken'); + if (err.name === 'TokenExpiredError') + Response.Unauthorized(res, 'Expired refreshToken'); + else Response.Unauthorized(res, 'Invalid refreshToken'); + return; + } + User.removeSession(decoded.userId, decoded.sessionId) + .then(msg => { + res.clearCookie('token'); + res.clearCookie('refreshToken'); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }); + + // Authenticate user -> return JWT token + app.post('/api/users/token', function (req, res) { + if (!req.body.password || !req.body.username) { + Response.BadParameters(res, 'Required parameters: username, password'); + return; + } + + // Validate types + if ( + typeof req.body.password !== 'string' || + typeof req.body.username !== 'string' || + (req.body.totpToken && typeof req.body.totpToken !== 'string') + ) { + Response.BadParameters(res, 'Parameters must be of type String'); + return; + } + + var user = new User(); + //Required params + user.username = req.body.username; + user.password = req.body.password; + + //Optional params + if (req.body.totpToken) user.totpToken = req.body.totpToken; + + user + .getToken(req.headers['user-agent']) + .then(msg => { + res.cookie('token', `JWT ${msg.token}`, { + secure: true, + httpOnly: true, + sameSite: 'None', + }); + res.cookie('refreshToken', msg.refreshToken, { + secure: true, + httpOnly: true, + path: '/api/users/refreshtoken', + sameSite: 'None', + }); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }); + + // Check if there are any existing users for creating first user + app.get('/api/users/init', function (req, res) { + User.getAll() + .then(msg => Response.Ok(res, msg.length === 0)) + .catch(err => Response.Internal(res, err)); + }); + + // Get all users + app.get('/api/users', acl.hasPermission('users:read'), function (req, res) { + User.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }); + + // Get all reviewers + app.get( + '/api/users/reviewers', + acl.hasPermission('users:read'), + function (req, res) { + User.getAll() + .then(users => { + var reviewers = []; + users.forEach(user => { + if ( + acl.isAllowed(user.role, 'audits:review') || + acl.isAllowed(user.role, 'audits:review-all') + ) { + reviewers.push(user); + } + }); + Response.Ok(res, reviewers); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get user self + app.get( + '/api/users/me', + acl.hasPermission('validtoken'), + function (req, res) { + User.getByUsername(req.decodedToken.username) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + //get TOTP Qrcode URL + app.get( + '/api/users/totp', + acl.hasPermission('validtoken'), + function (req, res) { + User.getTotpQrcode(req.decodedToken.username) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + //setup TOTP + app.post( + '/api/users/totp', + acl.hasPermission('validtoken'), + function (req, res) { + if (!req.body.totpToken || !req.body.totpSecret) { + Response.BadParameters(res, 'Missing some required parameters'); + return; + } + + User.setupTotp( + req.body.totpToken, + req.body.totpSecret, + req.decodedToken.username, + ) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + //cancel TOTP + app.delete( + '/api/users/totp', + acl.hasPermission('validtoken'), + function (req, res) { + if (!req.body.totpToken) { + Response.BadParameters(res, 'Missing some required parameters'); + return; + } + + User.cancelTotp(req.body.totpToken, req.decodedToken.username) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get user by username + app.get( + '/api/users/:username', + acl.hasPermission('users:read'), + function (req, res) { + User.getByUsername(req.params.username) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create user + app.post( + '/api/users', + acl.hasPermission('users:create'), + function (req, res) { + if ( + !req.body.username || + !req.body.password || + !req.body.firstname || + !req.body.lastname + ) { + Response.BadParameters(res, 'Missing some required parameters'); + return; + } + if (passwordpolicy.strongPassword(req.body.password) !== true) { + Response.BadParameters( + res, + 'Password does not match the password policy', + ); + return; + } + + var user = {}; + //Required params + user.username = req.body.username; + user.password = req.body.password; + user.firstname = req.body.firstname; + user.lastname = req.body.lastname; + + //Optionals params + user.role = req.body.role || 'user'; + if (req.body.email) user.email = req.body.email; + if (req.body.phone) user.phone = req.body.phone; + + User.create(user) + .then(msg => Response.Created(res, 'User created successfully')) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create First User + app.post('/api/users/init', function (req, res) { + if ( + !req.body.username || + !req.body.password || + !req.body.firstname || + !req.body.lastname + ) { + Response.BadParameters(res, 'Missing some required parameters'); + return; + } + if (passwordpolicy.strongPassword(req.body.password) !== true) { + Response.BadParameters( + res, + 'Password does not match the password policy', + ); + return; + } + var user = {}; + //Required params + user.username = req.body.username; + user.password = req.body.password; + user.firstname = req.body.firstname; + user.lastname = req.body.lastname; + user.role = 'admin'; + + User.getAll() + .then(users => { + if (users.length === 0) + User.create(user) + .then(msg => { + var newUser = new User(); + //Required params + newUser.username = req.body.username; + newUser.password = req.body.password; + + newUser + .getToken(req.headers['user-agent']) + .then(msg => { + res.cookie('token', `JWT ${msg.token}`, { + secure: true, + httpOnly: true, + }); + res.cookie('refreshToken', msg.refreshToken, { + secure: true, + httpOnly: true, + path: '/api/users/refreshtoken', + }); + Response.Created(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }) + .catch(err => Response.Internal(res, err)); + else Response.Forbidden(res, 'Already Initialized'); + }) + .catch(err => Response.Internal(res, err)); + }); + + // Update my profile + app.put( + '/api/users/me', + acl.hasPermission('validtoken'), + function (req, res) { + if ( + !req.body.currentPassword || + (req.body.newPassword && !req.body.confirmPassword) || + (req.body.confirmPassword && !req.body.newPassword) + ) { + Response.BadParameters(res, 'Missing some required parameters'); + return; + } + if ( + req.body.newPassword && + passwordpolicy.strongPassword(req.body.newPassword) !== true + ) { + Response.BadParameters( + res, + 'New Password does not match the password policy', + ); + return; + } + if ( + req.body.newPassword && + req.body.confirmPassword && + req.body.newPassword !== req.body.confirmPassword + ) { + Response.BadParameters(res, 'New password validation failed'); + return; + } + + var user = {}; + // Required params + user.password = req.body.currentPassword; + + // Optionals params + if (req.body.username) user.username = req.body.username; + if (req.body.newPassword) user.newPassword = req.body.newPassword; + if (req.body.firstname) user.firstname = req.body.firstname; + if (req.body.lastname) user.lastname = req.body.lastname; + if (!_.isNil(req.body.email)) user.email = req.body.email; + if (!_.isNil(req.body.phone)) user.phone = req.body.phone; + + User.updateProfile(req.decodedToken.username, user) + .then(msg => { + res.cookie('token', msg.token, { secure: true, httpOnly: true }); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update any user (admin only) + app.put( + '/api/users/:id', + acl.hasPermission('users:update'), + function (req, res) { + if ( + req.body.password && + !passwordpolicy.strongPassword(req.body.password) + ) { + Response.BadParameters( + res, + 'New Password does not match the password policy', + ); + return; + } + var user = {}; + + // Optionals params + if (req.body.username) user.username = req.body.username; + if (req.body.password) user.password = req.body.password; + if (req.body.firstname) user.firstname = req.body.firstname; + if (req.body.lastname) user.lastname = req.body.lastname; + if (!_.isNil(req.body.email)) user.email = req.body.email; + if (!_.isNil(req.body.phone)) user.phone = req.body.phone; + if (req.body.role) user.role = req.body.role; + if (typeof req.body.totpEnabled === 'boolean') + user.totpEnabled = req.body.totpEnabled; + if (typeof req.body.enabled === 'boolean') + user.enabled = req.body.enabled; + + User.updateUser(req.params.id, user) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); +}; diff --git a/backend/src/routes/vulnerability.js b/backend/src/routes/vulnerability.js new file mode 100644 index 0000000000000000000000000000000000000000..288d87593572b0716c71b6482b34eed154bbdaa6 --- /dev/null +++ b/backend/src/routes/vulnerability.js @@ -0,0 +1,256 @@ +module.exports = function (app) { + var Response = require('../lib/httpResponse.js'); + var acl = require('../lib/auth').acl; + var Vulnerability = require('mongoose').model('Vulnerability'); + var VulnerabilityType = require('mongoose').model('VulnerabilityType'); + var VulnerabilityCategory = require('mongoose').model( + 'VulnerabilityCategory', + ); + var VulnerabilityUpdate = require('mongoose').model('VulnerabilityUpdate'); + + // Get vulnerabilities list + app.get( + '/api/vulnerabilities', + acl.hasPermission('vulnerabilities:read'), + function (req, res) { + Vulnerability.getAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get vulnerabilities for export + app.get( + '/api/vulnerabilities/export', + acl.hasPermission('vulnerabilities:read'), + function (req, res) { + Vulnerability.export() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create vulnerabilities (array of vulnerabilities) + app.post( + '/api/vulnerabilities', + acl.hasPermission('vulnerabilities:create'), + function (req, res) { + for (var i = 0; i < req.body.length; i++) { + var vuln = req.body[i]; + if (!vuln.details) { + Response.BadParameters( + res, + 'Required parameters: details.locale, details.title', + ); + return; + } + var index = vuln.details.findIndex( + obj => + obj.locale && obj.locale !== '' && obj.title && obj.title !== '', + ); + if (index < 0) { + Response.BadParameters( + res, + 'Required parameters: details.locale, details.title', + ); + return; + } + } + + var vulnerabilities = []; + for (var i = 0; i < req.body.length; i++) { + var vuln = {}; + vuln.cvssv3 = req.body[i].cvssv3 || null; + if (req.body[i].priority) vuln.priority = req.body[i].priority; + if (req.body[i].remediationComplexity) + vuln.remediationComplexity = req.body[i].remediationComplexity; + if (req.body[i].category) { + vuln.category = req.body[i].category; + VulnerabilityCategory.create({ name: vuln.category }).catch(e => {}); + } + vuln.details = []; + req.body[i].details.forEach(d => { + if (!d.title || !d.locale) + // Array of details may contain entries without title or locale but we don't want to save them + return; + var details = {}; + if (d.locale) details.locale = d.locale; + if (d.title) details.title = d.title; + if (d.vulnType) { + details.vulnType = d.vulnType; + VulnerabilityType.create({ + locale: d.locale, + name: d.vulnType, + }).catch(e => {}); + } + if (d.description) details.description = d.description; + if (d.observation) details.observation = d.observation; + if (d.remediation) details.remediation = d.remediation; + if (d.references) details.references = d.references; + if (d.cwes) details.cwes = d.cwes; + if (d.customFields) details.customFields = d.customFields; + vuln.details.push(details); + }); + vuln.status = 0; + vulnerabilities.push(vuln); + } + Vulnerability.create(vulnerabilities) + .then(msg => Response.Created(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Update vulnerability + app.put( + '/api/vulnerabilities/:vulnerabilityId', + acl.hasPermission('vulnerabilities:update'), + function (req, res) { + if (!req.body.details) { + Response.BadParameters( + res, + 'Required parameters: details.locale, details.title', + ); + return; + } + var index = req.body.details.findIndex( + obj => obj.locale && obj.locale !== '' && obj.title && obj.title !== '', + ); + if (index < 0) { + Response.BadParameters( + res, + 'Required parameters: details.locale, details.title', + ); + return; + } + + var vuln = {}; + if (req.body.cvssv3) vuln.cvssv3 = req.body.cvssv3; + if (req.body.priority) vuln.priority = req.body.priority; + if (req.body.remediationComplexity) + vuln.remediationComplexity = req.body.remediationComplexity; + vuln.category = req.body.category || null; + vuln.details = []; + req.body.details.forEach(d => { + if (!d.title || !d.locale) return; + var details = {}; + if (d.locale) details.locale = d.locale; + if (d.title) details.title = d.title; + if (d.vulnType) details.vulnType = d.vulnType; + if (d.description) details.description = d.description; + if (d.observation) details.observation = d.observation; + if (d.remediation) details.remediation = d.remediation; + if (d.cwes) details.cwes = d.cwes; + if (d.references) details.references = d.references; + if (d.customFields) details.customFields = d.customFields; + vuln.details.push(details); + }); + vuln.status = 0; + + Vulnerability.update(req.params.vulnerabilityId, vuln) + .then(msg => { + if (req.body.status === 2) + VulnerabilityUpdate.deleteAllByVuln(req.params.vulnerabilityId); + Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete vulnerability + app.delete( + '/api/vulnerabilities/:vulnerabilityId', + acl.hasPermission('vulnerabilities:delete'), + function (req, res) { + Vulnerability.delete(req.params.vulnerabilityId) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Delete all vulnerabilities + app.delete( + '/api/vulnerabilities', + acl.hasPermission('vulnerabilities:delete-all'), + function (req, res) { + Vulnerability.deleteAll() + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get vulnerabilities list by language + app.get( + '/api/vulnerabilities/:locale', + acl.hasPermission('vulnerabilities:read'), + function (req, res) { + Vulnerability.getAllByLanguage(req.params.locale) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Create or Update vulnerability from finding for validation + app.post( + '/api/vulnerabilities/finding/:locale', + acl.hasPermission('vulnerability-updates:create'), + function (req, res) { + if (!req.body.title) { + Response.BadParameters(res, 'Required parameters: title'); + return; + } + + var vuln = {}; + // Required params + vuln.title = req.body.title; + vuln.locale = req.params.locale; + + // Optional params + vuln.cvssv3 = req.body.cvssv3 || ''; + vuln.priority = req.body.priority || null; + vuln.remediationComplexity = req.body.remediationComplexity || null; + vuln.references = req.body.references || []; + vuln.cwes = req.body.cwes || []; + vuln.vulnType = req.body.vulnType || null; + vuln.description = req.body.description || null; + vuln.observation = req.body.observation || null; + vuln.remediation = req.body.remediation || null; + vuln.category = req.body.category || null; + vuln.customFields = req.body.customFields || []; + + VulnerabilityUpdate.create(req.decodedToken.username, vuln) + .then(msg => { + if (msg === 'Finding created as new Vulnerability') + Response.Created(res, msg); + else Response.Ok(res, msg); + }) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Get vulnerability updates form vuln id + app.get( + '/api/vulnerabilities/updates/:vulnId', + acl.hasPermission('vulnerabilities:update'), + function (req, res) { + VulnerabilityUpdate.getAllByVuln(req.params.vulnId) + .then(msg => Response.Ok(res, msg)) + .catch(err => Response.Internal(res, err)); + }, + ); + + // Merge vulnerability with locale part of another one + app.put( + '/api/vulnerabilities/merge/:vulnId', + acl.hasPermission('vulnerabilities:update'), + function (req, res) { + if (!req.body.vulnId || !req.body.locale) { + Response.BadParameters(res, 'Required parameters: vulnId, locale'); + return; + } + + Vulnerability.Merge(req.params.vulnId, req.body.vulnId, req.body.locale) + .then(() => Response.Ok(res, 'Vulnerability merge successfully')) + .catch(err => Response.Internal(res, err)); + }, + ); +}; diff --git a/backend/src/translate/es_CL.json b/backend/src/translate/es_CL.json new file mode 100644 index 0000000000000000000000000000000000000000..e5446c7a81dfd1b3cc02fcd7159aa287a408c20d --- /dev/null +++ b/backend/src/translate/es_CL.json @@ -0,0 +1,24 @@ +{ + "ok": "Arreglao", + "ko": "No arreglado", + "partial": "Arreglo parcial", + "unknown": "Desconocido", + "Attack Vector": "Vector de ataque", + "Attack Complexity": "Complejidad de ataque", + "Privileges Required": "Privilegios requeridos", + "User Interaction": "Interacción de usuario", + "Scope": "Scope", + "Confidentiality": "Confidencialidad", + "Integrity": "Integridad", + "Availability": "Disponibilidad", + "Network": "Red", + "Adjacent Network": "Red adyacente", + "Physical": "Físico", + "Low": "Bajo", + "High": "Alto", + "None": "Ninguno", + "Required": "Requerido", + "Unchanged": "Sin cambiar", + "Changed": "Cambiado", + "No Category": "Sin categoría" +} diff --git a/backend/src/translate/fr.json b/backend/src/translate/fr.json new file mode 100644 index 0000000000000000000000000000000000000000..4e31019e32b6a53d357a7db256d0f7a05dbebca5 --- /dev/null +++ b/backend/src/translate/fr.json @@ -0,0 +1,20 @@ +{ + "Attack Vector": "Vecteur d'attaque", + "Attack Complexity": "Complexité d'attaque", + "Privileges Required": "Privilèges requis", + "User Interaction": "Interaction utilisateur", + "Scope": "Portée", + "Confidentiality": "Confidentialité", + "Integrity": "Intégrité", + "Availability": "Disponibilité", + "Network": "Réseau", + "Adjacent Network": "Réseau Local", + "Physical": "Physique", + "Low": "Faible", + "High": "Haut", + "None": "Aucun", + "Required": "Requis", + "Unchanged": "Inchangé", + "Changed": "Changé", + "No Category": "Non Catégorisé" +} diff --git a/backend/src/translate/index.js b/backend/src/translate/index.js new file mode 100644 index 0000000000000000000000000000000000000000..5814c1b5d4c2210eb5bbb2e08632d60bbf320e3c --- /dev/null +++ b/backend/src/translate/index.js @@ -0,0 +1,17 @@ +var fs = require('fs'); +var gLocale = 'en'; + +function setLocale(locale) { + gLocale = locale; +} +exports.setLocale = setLocale; + +function translate(message, locale = gLocale) { + try { + let dictionary = JSON.parse(fs.readFileSync(`${__dirname}/${locale}.json`)); + return dictionary[message] || message; + } catch (error) { + return message; + } +} +exports.translate = translate; diff --git a/backend/src/translate/nl.json b/backend/src/translate/nl.json new file mode 100644 index 0000000000000000000000000000000000000000..5d56a946c9561434f6fd3ccf085a513768ed474a --- /dev/null +++ b/backend/src/translate/nl.json @@ -0,0 +1,22 @@ +{ + "Attack Vector": "Aanvalsvector", + "Attack Complexity": "Aanvalscomplexiteit", + "Privileges Required": "Rechten nodig", + "User Interaction": "Gebruikers interactie", + "Scope": "Scope", + "Confidentiality": "Vertrouwelijkheid", + "Integrity": "Integriteit", + "Availability": "Beschikbaarheid", + "Network": "Netwerk", + "Adjacent Network": "Lokaal netwerk", + "Physical": "Fysiek", + "Low": "Laag", + "High": "Hoog", + "Medium": "Midden", + "Critical": "Kritiek", + "None": "Geen", + "Required": "Verplicht", + "Unchanged": "Ongewijzigd", + "Changed": "Verandert", + "No Category": "Geen category" +} diff --git a/backend/ssl/server.cert b/backend/ssl/server.cert new file mode 100644 index 0000000000000000000000000000000000000000..4542bbc6e95bc8c625e6864de605cf1f06a55d87 --- /dev/null +++ b/backend/ssl/server.cert @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDZTCCAk2gAwIBAgIUa+pVPH6QK8QZSc+BtyGshu/OSV8wDQYJKoZIhvcNAQEL +BQAwQjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDjAMBgNVBAoM +BXNoYXJ0MQ4wDAYDVQQDDAVzaGFydDAeFw0yMDAxMDgyMTQ2MzFaFw0yMDAyMDcy +MTQ2MzFaMEIxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMQ4wDAYD +VQQKDAVzaGFydDEOMAwGA1UEAwwFc2hhcnQwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDGVz4FRB7YcVwXWHvLGeNdlBNy8+ap/YD8DL+un3siSvMXr65v +zow8wJBS3O8cnSiR2FqY7CUd6rNfOluxAkyXRTC0mIMmwV92m5SF4VtdQYrG3x3o +Z/zcYtjeYt6qT1DJB2ufodWfmkMSCXMkpEq/48mrHQh+qgv8s4S+mvI1FF8xGh73 +7We28vgVGv2IDSjO5t1iXXR78pPj7s/vU6A4NpYpacsaTVZw9KLS7jJ0IpIN4gtx +zzZjERBp+2zVzY/mmyRVG+X3A+65fcmr8+DliNqbivBPkMQUbeGStppYF6zx7Ujg +saibccfi8losUUblEjTckFf0NqxD5As77qBnAgMBAAGjUzBRMB0GA1UdDgQWBBSI +KxOu1kjSFUyT1y3rVB1D0thlfjAfBgNVHSMEGDAWgBSIKxOu1kjSFUyT1y3rVB1D +0thlfjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAC22tgmDWW +5MKbhr3NYRgzv4I3GYKRoswnURsJaI3/viJNy6B5mVJ8Ybr+dpuUK0Q7X2H50lon +9AZH198lyTryQTEzZBLukh25qISdY/gKiCkazYhhKXwDkqYD0E5DlEAtJOCXS3VD +8WlFvTuSd9uKCa4gYTfAJxB7Osi+7+bXXmA9sFCRQwu3cQGaxW8drqDdAk/+6749 +dZ5XG47a52WNj0UaGfFuZsEWdTX+lpCLrVp3kYLSPHXptweL5mAG8pbzQA74ni2K +ZM3cCdGB9kDJgD3outTN1mqnKihhWYogjVQUmC/YQlKj9AxZ2PhQTrn+7zP/urUB +xuCmVmq01ECa +-----END CERTIFICATE----- diff --git a/backend/ssl/server.key b/backend/ssl/server.key new file mode 100644 index 0000000000000000000000000000000000000000..3fb72b8fab454f75c291ea2a0f3ae6c2edbe1594 --- /dev/null +++ b/backend/ssl/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDGVz4FRB7YcVwX +WHvLGeNdlBNy8+ap/YD8DL+un3siSvMXr65vzow8wJBS3O8cnSiR2FqY7CUd6rNf +OluxAkyXRTC0mIMmwV92m5SF4VtdQYrG3x3oZ/zcYtjeYt6qT1DJB2ufodWfmkMS +CXMkpEq/48mrHQh+qgv8s4S+mvI1FF8xGh737We28vgVGv2IDSjO5t1iXXR78pPj +7s/vU6A4NpYpacsaTVZw9KLS7jJ0IpIN4gtxzzZjERBp+2zVzY/mmyRVG+X3A+65 +fcmr8+DliNqbivBPkMQUbeGStppYF6zx7Ujgsaibccfi8losUUblEjTckFf0NqxD +5As77qBnAgMBAAECggEBAId9F1f/ldajcZZdEovGjoPhYHvZU0vyaYdEqjh+p13g +tzkKAO5NuYzSuoRwekMgtJMUqabnQd+y8X9u3S4I9Qss772epZD27eCXHRlrvb7x +w/xgdAzL5HckayNXhOXwsq3xDLsKkWSqu4B5xhdUx99Xmu7yMVhdjzy74UVXkXdh +0kgKczGN+0BZmPgMjtpGcCh9ERaqLltFgIXUWTm24T+160mqS+n69c7VdoyOjBSX +OWN3K/Tze3Wr3eCtZY2x5hq+nIZJntgUCaGsvoTjVY3438Zri/4uVI+P50/Wk5eR +U0YruysVkzyfjsascwhzyuy0chnXN1eaAkgjoOb8iZECgYEA/RuTEc1HBV9doT0x +MUoFAp2P664AxZIQpq98rfjFhHhyig8cJaP8zJOCXd3VbpMYlSfMFuNLtZ+JMaIL +I0yrBbgu0vUVohb58VPsh50173nKkH3GADG5t7SjhVnrBwgOc8CF3POoCgvwOxn6 +b22Pb5FpXsahHdPM0Aosw2aHSbkCgYEAyJt0vvmvAiRpYxIvU5UnP4w/OLoFhRxU +ZUHpvDCA0E4QNWQNyZO2epZJeSXPVWPwdL/LD6HCPzV/yYdmpdhBcrFd5RnKzOW8 +LXo1R2xj9VAwvGHaxJFnY8LPhNEv4G2TsoCugkMvMqUPLVW10kzbRJrz04/y0qIY +dL3XmBpPyx8CgYEAjC9bk36ImXcqWoWT22LNx5cRAU3Ma6DszAViHtEsRKSZe9HG +mypqd7wzdl3JOocKFIKITRzy79M+n1jKpnBuQKq0dG793lqvcHQ1Cx+NoedoxAKF +SdJAtHi+ILueqrRRnNbCzY++QGJAWduXk2OxD/AP1khZMDYAuDEKfecpgzECgYAi +1LMRkaKsWYwe0oJ7HbOh2gUEIXBh60hQCIC/1yAWiudPjd7C+C2/3SZGraTxK5gN +fVuRjEGA3hYg9KyC8Shz9I3cAH1w1Ba3QrfLfethJZpAqzDj3mc4MBP9+KA6dGwn +myYxod6pCXW4JmCachPENq9NNXowzko2wtuTIkZmewKBgQCHrbBgAfGdLFF0zHgK +Pz0MUYUMzO6P+l9G0QEXj5mzsqcQv9OhnCC15aFkCr7dC31bz8N6BdG6H560bupp +XluTVNAesbdimTf0ULJ87MhdtvmqFCe1pvX20ILW89O9B6OTZJl2DAoqSYsE1GLM +pHsUwQOzNPRI3IHdpyxKPs6sKA== +-----END PRIVATE KEY----- diff --git a/backend/swagger-autogen.js b/backend/swagger-autogen.js new file mode 100644 index 0000000000000000000000000000000000000000..47d371db83d62b188ee9d797dfa3f9dfbbd9a67b --- /dev/null +++ b/backend/swagger-autogen.js @@ -0,0 +1,17 @@ +const fs = require('fs'); +const swaggerAutogen = require('swagger-autogen')(); + +const routesFolder = './src/routes/'; + +const outputFile = '../docs/api/swagger.json'; +const endpointsFiles = []; + +var files = fs.readdirSync(routesFolder); +files.forEach(file => { + let fileStat = fs.statSync(routesFolder + '/' + file).isDirectory(); + if (!fileStat) { + endpointsFiles.push(routesFolder + '/' + file); + } +}); + +swaggerAutogen(outputFile, endpointsFiles); diff --git a/backend/tests/audit.test.js b/backend/tests/audit.test.js new file mode 100644 index 0000000000000000000000000000000000000000..01358352748e1c1b63cb08b8e1736a46419ad35a --- /dev/null +++ b/backend/tests/audit.test.js @@ -0,0 +1,121 @@ +/* + At the end + 1 Audit: {name: "Audit 1", language: "en", auditType: "Web"} +*/ + +module.exports = function (request, app) { + describe('Audit Suite Tests', () => { + var userToken = ''; + + var audit1Id = ''; + var audit2Id = ''; + + beforeAll(async () => { + var response = await request(app) + .post('/api/users/token') + .send({ username: 'admin', password: 'Admin123' }); + userToken = response.body.datas.token; + }); + + describe('Audit CRUD operations', () => { + it('Get Audits (no existing audit in db)', async () => { + var response = await request(app) + .get('/api/audits') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(0); + }); + + it('Create audit with partial information', async () => { + var audit = { name: 'Audit 1' }; + var response = await request(app) + .post('/api/audits') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(audit); + + expect(response.status).toBe(422); + }); + + it('Create audit with invalid audit type', async () => { + var audit = { + name: 'Audit 1', + language: 'en', + auditType: 'Internal Test', + }; + var response = await request(app) + .post('/api/audits') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(audit); + + expect(response.status).toBe(404); + }); + + it('Create audit', async () => { + var audit = { name: 'Audit 1', language: 'en', auditType: 'Web' }; + var response = await request(app) + .post('/api/audits') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(audit); + + expect(response.status).toBe(201); + audit1Id = response.body.datas.audit._id; + }); + + it('Create second audit', async () => { + var audit = { name: 'Audit 2', language: 'fr', auditType: 'Web' }; + var response = await request(app) + .post('/api/audits') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(audit); + + expect(response.status).toBe(201); + audit2Id = response.body.datas.audit._id; + }); + + it('Delete audit', async () => { + var response = await request(app) + .delete(`/api/audits/${audit2Id}`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + + response = await request(app) + .get('/api/audits') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(1); + }); + + it('Update audit general info', async () => { + var auditGeneralInfo = { + _id: audit1Id, + scope: ['Scope Item 1', 'Scope Item 2'], + }; + + var response = await request(app) + .put(`/api/audits/${audit1Id}/general`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send(auditGeneralInfo); + + expect(response.status).toBe(200); + }); + + it('Get audit general info', async () => { + var response = await request(app) + .get(`/api/audits/${audit1Id}/general`) + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + + expect(response.body.datas.name).toBe('Audit 1'); + expect(response.body.datas.auditType).toBe('Web'); + expect(response.body.datas.language).toBe('en'); + expect(response.body.datas.collaborators).toHaveLength(0); + expect(response.body.datas.reviewers).toHaveLength(0); + expect(response.body.datas.customFields).toHaveLength(0); + expect(response.body.datas.scope).toHaveLength(2); + expect(response.body.datas.scope[0]).toBe('Scope Item 1'); + expect(response.body.datas.scope[1]).toBe('Scope Item 2'); + }); + }); + }); +}; diff --git a/backend/tests/client.test.js b/backend/tests/client.test.js new file mode 100644 index 0000000000000000000000000000000000000000..c39bffcf419cbcf6d1f161c2f451cf2510c1bc52 --- /dev/null +++ b/backend/tests/client.test.js @@ -0,0 +1,198 @@ +/* + At the end + 2 Clients: [ + { + email: "client_updated@example.com", + lastname: "Client", + firstname: "Updated", + phone: "5146669999", + cell: "4389996666", + title: "IT manager", + company: {name: 'New Updated Company'} + }, + { + email: "client2@example.com", + lastname: "Client", + firstname: "User", + phone: "5146669999", + cell: "4389996666", + title: "IT manager", + company: {name: 'Company 1'} + } + ] + + 2 more Companies created: + 'New Company' + 'New Updated Company' +*/ + +module.exports = function (request, app) { + describe('Client Suite Tests', () => { + var userToken = ''; + beforeAll(async () => { + var response = await request(app) + .post('/api/users/token') + .send({ username: 'admin', password: 'Admin123' }); + userToken = response.body.datas.token; + }); + + describe('Client CRUD operations', () => { + var client1Id = ''; + var client2Id = ''; + var client3Id = ''; + it('Get clients (no existing clients in db)', async () => { + var response = await request(app) + .get('/api/clients') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(0); + }); + + it('Create client with email only', async () => { + var client = { email: 'client1@example.com' }; + var response = await request(app) + .post('/api/clients') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(client); + expect(response.status).toBe(201); + client1Id = response.body.datas._id; + }); + + it('Create client with all information and existing company', async () => { + var client = { + email: 'client2@example.com', + lastname: 'Client', + firstname: 'User', + phone: '5146669999', + cell: '4389996666', + title: 'IT manager', + company: { name: 'Company 1' }, + }; + var response = await request(app) + .post('/api/clients') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(client); + expect(response.status).toBe(201); + client2Id = response.body.datas._id; + }); + + it('Create client with nonexistent company', async () => { + var client = { + email: 'client3@example.com', + company: { name: 'New Company' }, + }; + var response = await request(app) + .post('/api/clients') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(client); + expect(response.status).toBe(201); + client3Id = response.body.datas._id; + }); + + it('Should not create client with existing email', async () => { + var client = { email: 'client1@example.com' }; + var response = await request(app) + .post('/api/clients') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(client); + + expect(response.status).toBe(422); + }); + + it('Should not create client without email', async () => { + var client = { firstname: 'firstname', lastname: 'lastname' }; + var response = await request(app) + .post('/api/clients') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(client); + + expect(response.status).toBe(422); + }); + + it('Get clients (existing clients in db)', async () => { + const expected = [ + { email: 'client1@example.com' }, + { + email: 'client2@example.com', + lastname: 'Client', + firstname: 'User', + phone: '5146669999', + cell: '4389996666', + title: 'IT manager', + company: { name: 'Company 1' }, + }, + { + email: 'client3@example.com', + company: { name: 'New Company' }, + }, + ]; + var response = await request(app) + .get('/api/clients') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect( + response.body.datas.map(t => { + return { + email: t.email, + lastname: t.lastname, + firstname: t.firstname, + phone: t.phone, + cell: t.cell, + title: t.title, + company: t.company, + }; + }), + ).toEqual(expect.arrayContaining(expected)); + }); + + it('Update client', async () => { + var client = { + email: 'client_updated@example.com', + lastname: 'Client', + firstname: 'Updated', + phone: '5146669999', + cell: '4389996666', + title: 'IT manager', + company: { name: 'New Updated Company' }, + }; + + var response = await request(app) + .put(`/api/clients/${client1Id}`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send(client); + expect(response.status).toBe(200); + }); + + it('Update client with nonexistent id', async () => { + var client = { firstname: 'Client' }; + + var response = await request(app) + .put(`/api/clients/deadbeefdeadbeefdeadbeef`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send(client); + expect(response.status).toBe(404); + }); + + it('Delete client', async () => { + var response = await request(app) + .delete(`/api/clients/${client3Id}`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + + response = await request(app) + .get('/api/clients') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(2); + }); + + it('Delete client with nonexistent email', async () => { + var response = await request(app) + .delete(`/api/clients/deadbeefdeadbeefdeadbeef`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(404); + }); + }); + }); +}; diff --git a/backend/tests/company.test.js b/backend/tests/company.test.js new file mode 100644 index 0000000000000000000000000000000000000000..c64e901345299ee319291126925b9128b802c1b5 --- /dev/null +++ b/backend/tests/company.test.js @@ -0,0 +1,118 @@ +/* + At the end + 1 Company: {name: 'Company 1', logo: 'fsociety logo'} +*/ + +module.exports = function (request, app) { + describe('Company Suite Tests', () => { + var userToken = ''; + beforeAll(async () => { + var response = await request(app) + .post('/api/users/token') + .send({ username: 'admin', password: 'Admin123' }); + userToken = response.body.datas.token; + }); + + describe('Company CRUD operations', () => { + var logo = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAUp0lEQVR4nO2dBZAjtxKG+yCcC9MFKsx4YWZm5qTCzMy5MDMzMzNzLgwXZmZmplefyr2lnTc8tiR7/Fe5dtc7nhlLrVb33zB9Bg4c+J90UVv09X0DXfhFVwBqjq4A1BxdAag5ugJQc3QFoOboCkDN0RWAmqMrADVHVwBqjv6+b8A1+vTp0/MC//3Xmwnn7+h7nYyOFIC+ffvKJJNMItNMM41MOeWU5vcJJphAxhlnHBlttNFkpJFGkmGHHdYc++eff5qf//77r/z+++/y888/y7fffitffvmlfPLJJ/LBBx/I22+/LW+88YZ8/vnnnr9Z89GnE4JB/fv3l5lnnlnmn39+mWuuuWTQoEEyyiij9KzyZgCt8MUXX8jzzz8vTz31lAwZMkReffVVIzjtjLYVAFbwAgssIMsvv7wstthiMuaYY/b8r5kTHwfdIhCIe+65R2699VZ54okn5K+//mrpdVuBthOAqaaaStZZZx1ZZZVVZOyxxzbvtXrCs4BAfPrpp3LttdfKFVdcIR9++KHX+ymCthAA9vT55ptPttpqK7PqUfmhApvijjvukNNPP11eeukl37eTiaAFgJXNvr7bbrvJbLPN1st6DxW6Pfz9999GEI444ghjSIaKYAVg6qmnlv32208WWWQRowHaFXgVJ5xwgpxzzjlGKEJDcAIw3HDDyY477mjUPb9LAHt8FahGwGvge3322We+b6kXghKAiSee2Oyds8wyS1tPehwQBCZ/iy22kOeee8737fQgGN0655xzys0332x8+E6bfGlosfHHH1+uvPJK47aGgiAEYKGFFpJLL71UxhprLN+30nKMPPLIcvbZZ8uSSy7p+1YMvAsAzB0DwsB04sqPwwgjjGC2unnnndf3rfi1AeDoUft1WPlxIOaw8sorm1iDL3jTAMMPP7yceeaZvSjcumGMMcaQs846y2g/X/AmALvvvrvMNNNMtVH7SSBiecghh3gbBy8CwMRvuummPi4dJFZffXVZaqmlvFzbuQAg6TB8wwwzjOtLBwnGA6bzoIMO8rIVOBeAWWed1Vi/dVf9NhiLiSaayIS2XcO5AMw444xtze23EriHruFlC+giHHSXYs3RFYCAoAmqLtFvwIABg11e8JtvvpF7771XPv74Y8MEquVb163hn3/+MaHi0047Ta677jrnOQNeqeABAwbIwQcfLGuuuWYtBYDwMNlODzzwgLd78J4P0K9fPxMJJCKYBGLpv/76qzz22GPy9NNPy+uvvy5ff/21yQ2ETp100klNDgHpY/zdCmHSgpGffvpJHnzwQXnmmWfkzTfflB9//NFcjwTVySabzISzF1xwQRl11FHN55LuhZqD1VZbzXsCqffsSlTgiSee2EsA7Moc8umIGdx4441msNNAqjhh1h122EFmmGGGyvdm3wdZv0Twrr76avnll19SP4c7t+yyy8q2225rUtskRhCOPfZY75MvIWgAaUwcK8qOCn7//fdmkC655JLC+fZolU022UT23ntvE3QqCwQAASWf77jjjjNaqOh9rL322rLPPvvI6KOP3vM+xSQkwCBU0eO5nks4FwCyYlCjvGxcddVVJuWbwUHF7rrrrqbwwgZqdvHFFzeCcuedd8pbb72Vei1YR3INBg4cWHhbYPK/++47k8d33333pR473njjyXLLLSd//PGH3H///f83seOOO64cc8wxJhOI8yLQxEN0DNjKSH4dccQR5aabbip0n1Xh3A1ERbPasXrZKxUMGoODqt944417TT776hlnnCFPPvmk0Qp77bWX3H333bLTTjuZVZMEcu9WWGEFefzxxwsXfb744ovms1mTT5HKQw89ZIzZo48+2lzrwgsvNN9NhY7vstFGG5mtjPfQeNQngmmnnVauueYaOf/883tpCVdwrgGo4eOLvvPOO6YAk1UDGLQ55phD5p577p6VgbYYPHiwLL300rHFIEzoLbfcIjvvvLP89ttvidfks+uuu66xDdAGkmCccT6My4svvtgIKMWiaedkiyHJE2o7ej402SOPPGLU/3vvvWfeIwCGhsBoJR6iNgCfJxZAPaPrYpIgbABw8sknm1VBmJhVTYj0wAMPNNW8aWDShg4dKltuuaXhFtKAPYA2IRppZx5rxu6hhx4qd911V6owASz8k046SZZYYonErUW1DXUBaAaECtXPd0Kg0RAh1BIGIwDU+h1wwAFmC8Cdm2666XLv2ww22gQhoHI3C6xA1LZqFT6/xhprGDczC5BXqOsk6z7u3sC7775rti3sAFQ+GiYEOBcAij3U0kVNank1/juThyFUFqxcVC4GZRrQMPAJGG96H1NMMUWqygfzzDOP2ccxQqtwDQjBa6+9ZvoVIFCwf2g/rs99uYRzIxDW79lnnzWTjdqn2hf88MMPJkmyCvC/cdfQJGkJJ2wr0XJyJiIJ/H/99deXyy67rKciuQpU82D/UPR63nnnmXqBZnAXReE8FsCEEw/AasbH53cGmD2fV1UWj89TSMrr0UcfNXtwFNgZEE/2tTDEiFFEgUbCNthll116hKrqPWLc3n777YZQoq/A5Zdfbu4Touvll1+udO6icL4FMHi2O4ZVjmFENoxtlKEKsZ7XWmut0gOO+6Uuo14TtwtfO5p+hUGGS2fbARiK+O/TTz99qeuj2jH+sDkw+myXlevAddiVw2yP6hW5gnMNoGDFwYWj/nAN1ZViP8a9oqyaQElW2riydRKzMtlXV1xxRaNaIY1Q/RdddFHsHs7kLLroooajQEXvv//+JlsXEid6LNfjPrPK1flOygbSX4i9X4+fcMIJe4QbzgFhcc0Cii8vgCJQkiBh9aKDSIcNVgY8+r777vt/n9UJx4q/4YYb5IUXXjCqk/2fYAyTiEVv9whSEohJy2ouwTEca69W/oaaRlVzXQo5uAeugXZYaaWVjEuYxFXAJtI9hBoAW9Pp9WhAhQfjo0DEuQAsvPDChltXJswGgwolCknEfoy6VqgKZ8JR66yaJGCoUZPPuaTins11oaZhHb/66qvE4xAE/H22DfuafJ4eQkwwXgSCYN+Pfi+EeOuttzbXcgnnXgAGWNzkg48++sisAlafegc2oGVXXXXV1MkHTBTBIFi3Kj3/+CzngJpOm3zwyiuvmC2NGIUNJltJJ7yfKMmkGhByCbrYNZwKABz47LPPnvh/DbNiJUczh2Hqtt9++0xfXUF6Fas2GlBiUhEk3lfh4CdqGK/BPo5jOEfeVC3uDXUfbQmDHSENozAt4wcq3HW9hFMBoGkjWUBJYA9nJaiBZYOkEbiCIsDFPOqoo3ppAc4NFYyxZ18D1QuHoOAzxx9/vDlHEeDORYkonXTKwNKKP/juEFIu4VQAsmoCMOQ22GADE4aNqm62hzKAWoYm1vOxmlHn9n0gCEwMgSA9jt/Zr8sg2gZGCS7a3mR5Da7JIKcCYBt1SSBiByvHalfrna2h6EqUxoASKbTdPiaDxI6oqsVFxFXTPRr3c5tttkkNNycBAcCoU2HiuxBaxn5JEwCOzzNGzYTzLSALMG+EeHWrgBAi2UKtYyaECWWlYE+gVuOMSt4j4ILdYE+iRgyjlC7HIGhoH2kID+zfKaecEptVxHtsWTS4ILsH11aFijDwMsssY9rKSkPwuZc8FVF5xqiZcJoTyCBlgUHSAYd7hwvAv8Z/JoxKvgCrE4NSVxMrjEQMeAFat2KMcXzU55ZGMqaq/Oh1pRG1wwjVz7Fy8QTYDphguAsoayaevAY9DtuCrQaG77bbbjMCy3FHHnmk4SXyTD7ngjV0CWcCwArTZIy8wKXabrvtZMMNN0xtC4vxhHDwYoUTq8cQw/reY489evnkqgGipI3mHdjuHpOKEYlgoYXoacAKjbsHJpjoImFtVD3biSazIgB5wRi5zA10RgQxGVjeRfZUBiEu2yYNuu/C2DH50MlEIHmfSYHvJ+OIlap9CAHvocqJ82t6FnF7YgGHH364oXGLFLXaLmaRDqd8Z3IZs3iHZsGZDUC8P+8AqvFXhsHTwYZxJAgENcxKJgCEhsCbIK/QnnxpFKkQlCKPEFpXkzf4yd9FK5rLtrXlOi7b5jgTAFRskQwfVCf9+KuArYHrEs4ltsDfrGqEIw5oigsuuMDc52abbWa4B/j+KsAuwK7IC66dlQbXTDgTgDQCyAZu2uabb26KRZrhEh122GFmLyYiRwwe9ZokiKw+NAaGJIUdcPsEcKqArB8SVNA6hHrzUNNVha4InAlAVqoXA0McAMudLttMWNH9PwrSyAk8kRHMZCbFIGzoCsRtY8vgcwSnyoLzQW7BKmIMavp7Glw2inAmAPqMniSQI4f1/P7775tBw+qumnlDmhUu5Z577mmMz7zn4ziOJy8Bo+z6668vHVTiXPpEE4xg8hMIHKWdL2qftBLOBCDJiFLXjFUC/SqNShvy5KsCRg6ypkwen/rkfDYaUCoK+AOqnvSe0EgUliYJgcsWOs6ulOTXws1Dudr8ebPYMFYdpFDZBzvpU8Sa0ckUxlKBoBMWJ8kkDi4fROVMAOIKK1kBGGioRhvNcoPIwyOGAMVbRoVrPII6haqIGsF4BiTGagaSjazq42bCGRMYl/LNqo8rkKjCgtlRP0KrROCgcSF5sopN9LMkbsBCMhHE99kG9H9l7ZK4YBYdQUg2IUpqQ+MRLuBMAGDhkHZ7f6MeMC5tW2vpioLzY2AxsASUuKZOHNQvcQVSs5JA3J5EULgAWwjh8wk+QUmT/5fHm7DBPRCriLseSarRPISqNkcROKOCsWzp7MFPviQrlKKIaCm1NOIGZO3EpYXZ0Mlle4Gvp84AAUjaQ7k23cmZzGheHi/8dWjgNEBpY7Cut956xsfP0gjcC+wiXcHj7ovzUSSjkUTGBXshb+ZTVTizASBB7FQpCiDiJl8aWwAJkqRyp+3dsGyEa9nr4f2prE0zoLgH8gNsdcz5WYlUKbHys8DqhNTBLuCaCHXSPfI+3wEWMum+4Pz5Hgoqhl1NvrgOBzNBuqqzyqDhBeglAG1LNi159KwSDDO2CAxHcgWKDhb0MiwftgGagAkkFZ1s4yLAPiBcDdeAMMBeEiJGI5AMwvmwI9A4adXGCAZ7Ps82RmBcVwY5FQBUoWbF5MnwYcVSrs2rmYB3IC+wGdAaBV6wnXw3tqQiXocdmnb9QCmnAkAdnA5MFjPYjijaQ0gaJJG6vYwNY+QSTlPC7Eew58kOqgNQ/SoA2ANsfS7hVADY74i0oSaJyoX8DGBXoHpJC2ZJPXNdH+i8MohmT2TfYNQhBHUGnAh5g9JQ/647hIkPAcDNofgTfxc+vI4tYhXwIFRGA9hKsoldw0t5OJEw1D9kysMPPxzc83RdAOPv1FNPNUmgpI/TbczHw6W9tYuHwKHIE5q1KLXaCUD7sfrhAMh8dkn+2PAmAMrKEfdHGFymQfkGhBEp5hjFJKuQBOML3jqESCNCCGtGgIZUMMghwqQu4+EuQXYSJeQEfwgPE79A+H3Ce59A9kKSNbUPDzQvkTxcInziaE/htPNQ7UM2EZk8hFjxMuj9g51BoCirAaSC7GHqCUjlQkBxXaF0iWVwvrwsH7l9BHtQ9dDFnE9bzhAFxQWkUsknvAsA4KGJROHsCh5pMGsYiASN2Cv5G0OJaCGRPWwH/Gb4fGmEVzkWwomYAYOOm0WDBo6h6wheCAEdzodAcE0mii2IzB8EiLo+hIcJpy0MnD40L8mi/OTarGaidhA5XJd7xrDlfQQIcoc8An63v5f+fu6555roo28EIQD4w8TwSdooWgXERBKjT8rc5XysOvZdGjAQACJPEG3BhPF/hAibBOEhWERCCJVDRPLSiBk4Da5dFFyL1e9z71cEIQCAgeSBDGXaujNpZNvmVfEIHHswGoTfscBR9UUe2oQwUWRSJqZBcItMoBAQzFPDKMEqkwmDwJDqRVOovHWHGJlsEax4IoMkaRaZfKqHSWUr286FMHIoCEYAWL0YW2WAEFDGjRC0usfO5JNPblY+VHYZFhPPx+5F5BvBCADQBzuUBS4W1n7eMrQiYLLZZmj5ltZXOAt8R1+kTxyCEoCyfYDEqsbF8sdqJ4uoWcBoxGrHRrGt+jLI087eJYISgGbEBDDq4AFoEEGjiLIdN5hk/HeKVOEkcA2rVuyg3YqmnrUawXgB0iBgsOibESHUrQTihgczQuRQLIqQJW0zcAEYlGgRJpxmEWXr/JPuCXc068kmLhGUAECiQNi0Kl0MYYBuZqvhJ/44XAAED5Y9JJD2J2pFmJrrIWB53VUXCColhwkiRRoLuxXAQ2CS9UkhroH2cd0OPgtB2QCwbnEVNJ0C8iBCC3QFJQCAQFCnwnXKdx4EJwDk14dkJDULGIBEJUNDcAKAHcBTRKoQQqGB70KYO6vNvQ8EJwAANk/rBzoFdBwLbf+XUAWAuD/PDOoU4Nkg1CEiSAGQRvOEqk/8CAGselrVuez6UQTBCgATT1oW5VLtKATac4CWdwhzqAhWAKTRVYQGUqGRJ3mB348QhyzAXrOC8wDaFkGIe8RciNCVT7Injant5g8hIngBkMYTueDP6QQSsgDoSifxlF6A0YdHhYi2EABpdO4iG5dcPGlRsKYqEABS2Zn8KrkNLtE2AiCNbBr6+A8aNMj8HYoQ6MqnuQOdxFz1+m8G2koAGGioYqJ6PC/IZUvVLBDDoO9Q3kKWUNBWAiANIRgyZIiprCFxw6dhqM8jhrqmsXQ7eittJwAKbAIKN+giZj9AyhW0vRwkD7V+Pp783QwElRFUBmTYkKyZ9DCnVgENxGPlyBJuZ4SziZaE9v2j7azW6LWKeNFzDx061LS7a/fJl07QADZ4xAydNigSyXpCSVEw8TS3RNvwXMB2VflRdJQAKEjyRCvw4kGT+giWooWnvCgfo2yNWn4yekKmdcugIwXABlm+5PdTIo6GoKqHpFD4BLQE9YQkoRCto2wLCpdnF9GylXa0/N5pk26j4wUgDrbraNfut9J+CBVBpYW7Qh0nOglt7wV0UQ1dAag5ugJQc3QFoOboCkDN0RWAmqMrADVHVwBqjq4A1BxdAag5/gfOJVH+CNY0UQAAAABJRU5ErkJggg=='; + var company1Id = ''; + var company2Id = ''; + it('Get companies (no existing companies in db)', async () => { + var response = await request(app) + .get('/api/companies') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(0); + }); + + it('Create company with name only', async () => { + var company = { name: 'Company 1' }; + var response = await request(app) + .post('/api/companies') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(company); + + expect(response.status).toBe(201); + company1Id = response.body.datas._id; + }); + + it('Create company with name and logo', async () => { + var company = { name: 'Company 2', logo: 'VGVzdCBpbWFnZQ==' }; + var response = await request(app) + .post('/api/companies') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(company); + + expect(response.status).toBe(201); + company2Id = response.body.datas._id; + }); + + it('Should not create company with existing name', async () => { + var company = { name: 'Company 1' }; + var response = await request(app) + .post('/api/companies') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(company); + + expect(response.status).toBe(422); + }); + + it('Get companies (existing companies in db)', async () => { + const expected = [ + { name: 'Company 1' }, + { name: 'Company 2', logo: 'VGVzdCBpbWFnZQ==' }, + ]; + var response = await request(app) + .get('/api/companies') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect( + response.body.datas.map(t => { + return { name: t.name, logo: t.logo }; + }), + ).toEqual(expect.arrayContaining(expected)); + }); + + it('Update company with logo only', async () => { + var company = { logo: logo }; + var response = await request(app) + .put(`/api/companies/${company1Id}`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send(company); + expect(response.status).toBe(200); + }); + + it('Update company with nonexistent id', async () => { + var company = { name: 'company Updated' }; + + var response = await request(app) + .put(`/api/companies/deadbeefdeadbeefdeadbeef`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send(company); + expect(response.status).toBe(404); + }); + + it('Delete company', async () => { + var response = await request(app) + .delete(`/api/companies/${company2Id}`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + + response = await request(app) + .get('/api/companies') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(1); + }); + + it('Delete company with nonexistent id', async () => { + var response = await request(app) + .delete(`/api/companies/deadbeefdeadbeefdeadbeef`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(404); + }); + }); + }); +}; diff --git a/backend/tests/data.test.js b/backend/tests/data.test.js new file mode 100644 index 0000000000000000000000000000000000000000..ac2e73a753f224bf6086fb14a61203f8f1fe5a1f --- /dev/null +++ b/backend/tests/data.test.js @@ -0,0 +1,493 @@ +/* + At the end + 2 Languages: [ + {locale: 'en', language: 'English'}, + {locale: 'fr', language: 'French'} + ] + 1 Audit type: {locale: 'en', name: 'Web'} + 1 Vulnerability type: {locale: 'en', name: 'Internal'} + 3 Sections: [ + {locale: 'en', name: 'Attack Scenario', field: 'attack_scenario'}, + {locale: 'en', name: 'Goal', field: 'goal'}, + {locale: 'fr', name: 'But', field: 'goal'} + ] +*/ + +module.exports = function (request, app) { + describe('Data Suite Tests', () => { + var userToken = ''; + beforeAll(async () => { + var response = await request(app) + .post('/api/users/token') + .send({ username: 'admin', password: 'Admin123' }); + userToken = response.body.datas.token; + }); + + describe('Language CRUD operations', () => { + it('Get languages', async () => { + var response = await request(app) + .get('/api/data/languages') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(0); + }); + + it('Create 3 languages', async () => { + var english = { + locale: 'en', + language: 'English', + }; + + var french = { + locale: 'fr', + language: 'French', + }; + + var espagnol = { + locale: 'es', + language: 'Espagnol', + }; + var response = await request(app) + .post('/api/data/languages') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(english); + expect(response.status).toBe(201); + + var response = await request(app) + .post('/api/data/languages') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(french); + expect(response.status).toBe(201); + + var response = await request(app) + .post('/api/data/languages') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(espagnol); + expect(response.status).toBe(201); + }); + + it('Should not create with existing locale', async () => { + var language = { + locale: 'fr', + language: 'French2', + }; + var response = await request(app) + .post('/api/data/languages') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(language); + + expect(response.status).toBe(422); + }); + + it('Should not create with existing name', async () => { + var language = { + locale: 'us', + language: 'English', + }; + var response = await request(app) + .post('/api/data/languages') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(language); + + expect(response.status).toBe(422); + }); + + it('Get languages', async () => { + const expected = [ + { locale: 'en', language: 'English' }, + { locale: 'fr', language: 'French' }, + { locale: 'es', language: 'Espagnol' }, + ]; + + var response = await request(app) + .get('/api/data/languages') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toEqual(expect.arrayContaining(expected)); + }); + + it('Delete language', async () => { + var response = await request(app) + .delete('/api/data/languages/es') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + + var response = await request(app) + .get('/api/data/languages') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(2); + }); + + it('Should not delete language with nonexistent locale', async () => { + var response = await request(app) + .delete('/api/data/languages/us') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(404); + }); + }); + + describe('Audit types CRUD operations', () => { + it('Get audit types', async () => { + var response = await request(app) + .get('/api/data/audit-types') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(0); + }); + + it('Create audit type Retest', async () => { + // Get the template ID first + response = await request(app) + .get('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]); + + var templates = response.body.datas; + + var auditType = { + name: 'Retest', + templates: templates, + stage: 'retest', + }; + + var response = await request(app) + .post('/api/data/audit-types') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(auditType); + + expect(response.status).toBe(201); + }); + + it('Create audit type Multi', async () => { + // Get the template ID first + response = await request(app) + .get('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]); + + var templates = response.body.datas; + + var auditType = { + name: 'Multi', + templates: templates, + stage: 'multi', + }; + + var response = await request(app) + .post('/api/data/audit-types') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(auditType); + + expect(response.status).toBe(201); + }); + + it('Create audit type with wrong stage', async () => { + // Get the template ID first + response = await request(app) + .get('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]); + + var templates = response.body.datas; + + var auditType = { + name: 'Wifi', + templates: templates, + stage: 'itdoesnotexist', + }; + + var response = await request(app) + .post('/api/data/audit-types') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(auditType); + + expect(response.status).toBe(201); + }); + + it('Create audit type Web', async () => { + // Get the template ID first + response = await request(app) + .get('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]); + + var templates = response.body.datas; + + var auditType = { + name: 'Web', + templates: templates, + }; + var response = await request(app) + .post('/api/data/audit-types') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(auditType); + + expect(response.status).toBe(201); + }); + + it('Should not create with existing name', async () => { + // Get the template ID first + response = await request(app) + .get('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]); + + var templates = response.body.datas; + + var auditType = { + name: 'Web', + templates: templates, + }; + var response = await request(app) + .post('/api/data/audit-types') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(auditType); + + expect(response.status).toBe(422); + }); + + it('Get audit types', async () => { + const expected = [ + { + hidden: ['network'], + name: 'Retest', + sections: [], + templates: [{}], + stage: 'retest', + }, + { + hidden: ['network'], + name: 'Multi', + sections: [], + templates: [{}], + stage: 'multi', + }, + { + hidden: [], + name: 'Wifi', + sections: [], + templates: [{}], + stage: 'default', + }, + { + hidden: [], + name: 'Web', + sections: [], + templates: [{}], + stage: 'default', + }, + ]; + var response = await request(app) + .get('/api/data/audit-types') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toEqual(expect.arrayContaining(expected)); + }); + + it('Delete audit type', async () => { + var response = await request(app) + .delete('/api/data/audit-types/Wifi') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + + var response = await request(app) + .get('/api/data/audit-types') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(3); + }); + + it('Should not delete audit type with nonexistent name', async () => { + var response = await request(app) + .delete('/api/data/audit-types/nonexistent') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(404); + }); + }); + + describe('Vulnerability types CRUD operations', () => { + it('Get vulnerability types', async () => { + var response = await request(app) + .get('/api/data/vulnerability-types') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(0); + }); + + it('Create vulnerability type Internal', async () => { + var type = { + locale: 'en', + name: 'Internal', + }; + var response = await request(app) + .post('/api/data/vulnerability-types') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(type); + + expect(response.status).toBe(201); + }); + + it('Create vulnerability type Web', async () => { + var type = { + locale: 'en', + name: 'Web', + }; + var response = await request(app) + .post('/api/data/vulnerability-types') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(type); + + expect(response.status).toBe(201); + }); + + it('Should not create with existing name', async () => { + var type = { + locale: 'en', + name: 'Web', + }; + var response = await request(app) + .post('/api/data/vulnerability-types') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(type); + + expect(response.status).toBe(422); + }); + + it('Get vulnerability types', async () => { + const expected = [ + { locale: 'en', name: 'Internal' }, + { locale: 'en', name: 'Web' }, + ]; + var response = await request(app) + .get('/api/data/vulnerability-types') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toEqual(expect.arrayContaining(expected)); + }); + + it('Delete vulnerability type', async () => { + var response = await request(app) + .delete('/api/data/vulnerability-types/Web') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + + var response = await request(app) + .get('/api/data/vulnerability-types') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(1); + }); + + it('Should not delete vulnerability type with nonexistent name', async () => { + var response = await request(app) + .delete('/api/data/vulnerability-types/nonexistent') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(404); + }); + }); + + describe('Sections CRUD operations', () => { + it('Get sections', async () => { + var response = await request(app) + .get('/api/data/sections') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(0); + }); + + it('Create section Attack Scenario locale en', async () => { + var section = { + name: 'Attack Scenario', + field: 'attack_scenario', + }; + var response = await request(app) + .post('/api/data/sections') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(section); + + expect(response.status).toBe(201); + }); + + it('Create section But locale fr', async () => { + var section = { + name: 'But', + field: 'goal', + }; + var response = await request(app) + .post('/api/data/sections') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(section); + + expect(response.status).toBe(201); + }); + + it('Should not create section with existing name', async () => { + var section = { + name: 'Attack Scenario', + field: 'goal', + }; + var response = await request(app) + .post('/api/data/sections') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(section); + + expect(response.status).toBe(422); + }); + + it('Should not create section with existing field', async () => { + var section = { + name: 'But2', + field: 'goal', + }; + var response = await request(app) + .post('/api/data/sections') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(section); + + expect(response.status).toBe(422); + }); + + it('Get sections', async () => { + const expected = [ + { name: 'Attack Scenario', field: 'attack_scenario' }, + { name: 'But', field: 'goal' }, + ]; + var response = await request(app) + .get('/api/data/sections') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toEqual(expect.arrayContaining(expected)); + }); + + //it('Should not delete nonexistent section', async () => { + // var response = await request(app).delete('/api/data/sections/attack_scenario/ru') + // .set('Cookie', [ + // `token=JWT ${userToken}` + // ]) + // expect(response.status).toBe(404) + //}) + + //it('Delete section', async () => { + // const expected = [ + // {locale: "en", name: 'Attack Scenario', field: 'attack_scenario'}, + // {locale: "fr", name: 'Scenario', field: 'attack_scenario'}, + // {locale: "en", name: 'Goal', field: 'goal'}, + // ] + + // var response = await request(app).delete('/api/data/sections/but/fr') + // .set('Cookie', [ + // `token=JWT ${userToken}` + // ]) + // expect(response.status).toBe(200) + + // var response = await request(app).get('/api/data/sections') + // .set('Cookie', [ + // `token=JWT ${userToken}` + // ]) + // expect(response.body.datas).toHaveLength(3) + // expect(response.body.datas).toEqual(expect.arrayContaining(expected)) + //}) + }); + }); +}; diff --git a/backend/tests/index.test.js b/backend/tests/index.test.js new file mode 100644 index 0000000000000000000000000000000000000000..0be4a12feb0193f4421162af488c3f0befdf6a19 --- /dev/null +++ b/backend/tests/index.test.js @@ -0,0 +1,27 @@ +const request = require('supertest'); + +var env = process.env.NODE_ENV || 'dev'; +var config = require('../src/config/config.json')[env]; + +var mongoose = require('mongoose'); +mongoose.connect( + `mongodb://${config.database.server}:${config.database.port}/${config.database.name}`, + {}, +); + +/* Clean the DB */ +mongoose.connection.dropDatabase(); + +const app = require(__dirname + '/../src/app'); + +// Import tests +require('./unauthenticated.test')(request, app); +require('./user.test')(request, app); +require('./template.test')(request, app); +require('./data.test')(request, app); +require('./company.test')(request, app); +require('./client.test')(request, app); +require('./vulnerability.test')(request, app); +require('./audit.test')(request, app); +require('./settings.test')(request, app); +require('./lib.test')(); diff --git a/backend/tests/lib.test.js b/backend/tests/lib.test.js new file mode 100644 index 0000000000000000000000000000000000000000..cee6fc659e31e80e129801be5f0592aa80f84623 --- /dev/null +++ b/backend/tests/lib.test.js @@ -0,0 +1,571 @@ +module.exports = function () { + var html2ooxml = require('../src/lib/html2ooxml'); + var utils = require('../src/lib/utils'); + + describe('Lib functions Suite Tests', () => { + describe('Name format validation tests', () => { + it('Valid Filename', () => { + var filename = 'Vulnerability 1'; + var result = utils.validFilename(filename); + expect(result).toEqual(true); + }); + + it('Valid Latin Filename', () => { + var filename = 'Vulnerabilité 1'; + var result = utils.validFilename(filename); + expect(result).toEqual(true); + }); + + it('Valid Latvian Filename', () => { + var filename = 'Pažeidžiamumas 1'; + var result = utils.validFilename(filename); + expect(result).toEqual(true); + }); + + it('Valid Filename with special chars', () => { + var filename = 'Vulnerability_1-test'; + var result = utils.validFilename(filename); + expect(result).toEqual(true); + }); + + it('Invalid Filename', () => { + var filename = ' 1'; + var result = utils.validFilename(filename); + expect(result).toEqual(false); + }); + }); + + describe('html2ooxml tests', () => { + it('Simple Paragraph', () => { + var html = '

Paragraph Text

'; + var expected = `Paragraph Text`; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Text without tag', () => { + var html = 'Paragraph Text'; + var expected = `Paragraph Text`; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Bold without wrapping paragraph', () => { + var html = 'Paragraph Bold'; + var expected = ''; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Bold', () => { + var html = '

Paragraph Bold

'; + var expected = + `` + + `` + + `Paragraph ` + + `` + + `` + + `` + + `` + + `` + + `` + + `Bold` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Italic', () => { + var html = '

Paragraph Italic

'; + var expected = + `` + + `` + + `Paragraph ` + + `` + + `` + + `` + + `` + + `` + + `` + + `Italic` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Underline', () => { + var html = '

Paragraph Underline

'; + var expected = + `` + + `` + + `Paragraph ` + + `` + + `` + + `` + + `` + + `` + + `Underline` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Strike', () => { + var html = '

Paragraph Strike

'; + var expected = + `` + + `` + + `Paragraph ` + + `` + + `` + + `` + + `` + + `` + + `Strike` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Bold and Italics', () => { + var html = '

Paragraph Mark

'; + var expected = + `` + + `` + + `Paragraph ` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Mark` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('All marks', () => { + var html = '

Paragraph Mark

'; + var expected = + `` + + `` + + `Paragraph ` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Mark` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Heading 1', () => { + var html = '

Heading

'; + var expected = + `` + + `` + + `` + + `` + + `` + + `Heading` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Heading 2', () => { + var html = '

Heading

'; + var expected = + `` + + `` + + `` + + `` + + `` + + `Heading` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Heading 3', () => { + var html = '

Heading

'; + var expected = + `` + + `` + + `` + + `` + + `` + + `Heading` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Heading 4', () => { + var html = '

Heading

'; + var expected = + `` + + `` + + `` + + `` + + `` + + `Heading` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Heading 5', () => { + var html = '
Heading
'; + var expected = + `` + + `` + + `` + + `` + + `` + + `Heading` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Heading 6', () => { + var html = '
Heading
'; + var expected = + `` + + `` + + `` + + `` + + `` + + `Heading` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Simple Bullets', () => { + var html = + `
    ` + + `
  • ` + + `

    Bullet1

    ` + + `
  • ` + + `
  • ` + + `

    Bullet2

    ` + + `
  • ` + + `
`; + var expected = + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Bullet1` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Bullet2` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Simple Bullets without ul tag', () => { + var html = + `
  • ` + + `

    Bullet1

    ` + + `
  • ` + + `
  • ` + + `

    Bullet2

    ` + + `
  • `; + var expected = + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Bullet1` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Bullet2` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Nested Bullets', () => { + var html = + `
      ` + + `
    • ` + + `

      Bullet1

      ` + + `
    • ` + + `
        ` + + `
      • ` + + `

        BulletNested

        ` + + `
      • ` + + `
      ` + + `
    • ` + + `

      Bullet2

      ` + + `
    • ` + + `
    `; + var expected = + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Bullet1` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `BulletNested` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Bullet2` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Simple Numbering', () => { + var html = + `
      ` + + `
    1. ` + + `

      Number1

      ` + + `
    2. ` + + `
    3. ` + + `

      Number2

      ` + + `
    4. ` + + `
    `; + var expected = + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Number1` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Number2` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Nested Numbering', () => { + var html = + `
      ` + + `
    1. ` + + `

      Number1

      ` + + `
    2. ` + + `
        ` + + `
      1. ` + + `

        NumberNested

        ` + + `
      2. ` + + `
      ` + + `
    3. ` + + `

      Number2

      ` + + `
    4. ` + + `
    `; + var expected = + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Number1` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `NumberNested` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Number2` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Break', () => { + var html = '

    Paragraph
    Break

    '; + var expected = + `` + + `` + + `Paragraph` + + `` + + `` + + `` + + `` + + `` + + `Break` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Break with newline', () => { + var html = '

    Paragraph\nBreak

    '; + var expected = + `` + + `` + + `Paragraph` + + `` + + `` + + `` + + `` + + `` + + `Break` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('Code', () => { + var html = '

    Paragraph Code Paragraph

    '; + var expected = + `` + + `` + + `Paragraph ` + + `` + + `` + + `` + + `` + + `` + + `Code` + + `` + + `` + + ` Paragraph` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + + it('CodeBlock', () => { + var html = '
    Code Block
    '; + var expected = + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `` + + `Code Block` + + `` + + ``; + var ooxml = html2ooxml(html); + expect(ooxml).toEqual(expected); + }); + }); + }); +}; diff --git a/backend/tests/role.test.js b/backend/tests/role.test.js new file mode 100644 index 0000000000000000000000000000000000000000..79244e8cb1998ba0a86aec36fa206509daeb8748 --- /dev/null +++ b/backend/tests/role.test.js @@ -0,0 +1,30 @@ +module.exports = function (request, app) { + describe('Roles Suite Tests', () => { + describe('User Roles', () => { + it('Get the users init state', async () => { + var response = await request(app).get('/api/users/init'); + + expect(response.status).toBe(200); + expect(response.body.datas).toBe(false); + }); + }); + + describe('Report Roles', () => { + it('Get the users init state', async () => { + var response = await request(app).get('/api/users/init'); + + expect(response.status).toBe(200); + expect(response.body.datas).toBe(false); + }); + }); + + describe('Admin Roles', () => { + it('Get the users init state', async () => { + var response = await request(app).get('/api/users/init'); + + expect(response.status).toBe(200); + expect(response.body.datas).toBe(false); + }); + }); + }); +}; diff --git a/backend/tests/settings.test.js b/backend/tests/settings.test.js new file mode 100644 index 0000000000000000000000000000000000000000..5df7121f5028b839151cd5d3fc3a2dc40373c271 --- /dev/null +++ b/backend/tests/settings.test.js @@ -0,0 +1,233 @@ +module.exports = function (request, app) { + describe('Application settings', () => { + var userToken = ''; + beforeAll(async () => { + var response = await request(app) + .post('/api/users/token') + .send({ username: 'admin', password: 'Admin123' }); + userToken = response.body.datas.token; + }); + + const defaultPublicSettings = { + report: { + enabled: true, + public: { + captions: ['Figure'], + cvssColors: { + criticalColor: '#212121', + highColor: '#fe0000', + lowColor: '#008000', + mediumColor: '#f9a009', + noneColor: '#4a86e8', + }, + highlightWarning: false, + highlightWarningColor: '#ffff25', + requiredFields: { + company: false, + client: false, + dateStart: false, + dateEnd: false, + dateReport: false, + findingType: false, + scope: false, + findingDescription: false, + findingObservation: false, + findingReferences: false, + findingProofs: false, + findingAffected: false, + findingRemediationDifficulty: false, + findingPriority: false, + findingRemediation: false, + }, + }, + }, + reviews: { + enabled: false, + public: { + mandatoryReview: false, + minReviewers: 1, + }, + }, + }; + + const defaultSettings = { + report: { + enabled: true, + private: { + imageBorder: false, + imageBorderColor: '#000000', + }, + public: { + captions: ['Figure'], + cvssColors: { + criticalColor: '#212121', + highColor: '#fe0000', + lowColor: '#008000', + mediumColor: '#f9a009', + noneColor: '#4a86e8', + }, + highlightWarning: false, + highlightWarningColor: '#ffff25', + requiredFields: { + company: false, + client: false, + dateStart: false, + dateEnd: false, + dateReport: false, + findingType: false, + scope: false, + findingDescription: false, + findingObservation: false, + findingReferences: false, + findingProofs: false, + findingAffected: false, + findingRemediationDifficulty: false, + findingPriority: false, + findingRemediation: false, + }, + }, + }, + reviews: { + enabled: false, + private: { + removeApprovalsUponUpdate: false, + }, + public: { + mandatoryReview: false, + minReviewers: 1, + }, + }, + }; + + it('Get settings', async () => { + var response = await request(app) + .get('/api/settings') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toEqual(defaultSettings); + }); + + it('Get public settings', async () => { + var response = await request(app) + .get('/api/settings/public') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toEqual(defaultPublicSettings); + }); + + it('Edit settings', async () => { + const fullModification = { + report: { + enabled: false, + private: { + imageBorder: true, + imageBorderColor: '#123456', + }, + public: { + captions: ['Figure', 'Test'], + cvssColors: { + criticalColor: '#123456', + highColor: '#123456', + lowColor: '#123456', + mediumColor: '#123456', + noneColor: '#123456', + }, + highlightWarning: true, + highlightWarningColor: '#123456', + requiredFields: { + company: true, + client: false, + dateStart: true, + dateEnd: true, + dateReport: false, + findingType: false, + scope: true, + findingDescription: false, + findingObservation: false, + findingReferences: false, + findingProofs: false, + findingAffected: false, + findingRemediationDifficulty: false, + findingPriority: false, + findingRemediation: false, + }, + }, + }, + reviews: { + enabled: true, + private: { + removeApprovalsUponUpdate: true, + }, + public: { + mandatoryReview: true, + minReviewers: 2, + }, + }, + }; + var response = await request(app) + .put('/api/settings') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(fullModification); + expect(response.status).toBe(200); + + var response = await request(app) + .get('/api/settings') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + expect(response.body.datas).toEqual(fullModification); + + const partialModification = { + reviews: { + public: { + mandatoryReview: false, + minReviewers: 5, + }, + }, + }; + var response = await request(app) + .put('/api/settings') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(partialModification); + expect(response.status).toBe(200); + + var response = await request(app) + .get('/api/settings') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + + expect(response.body.datas.reviews.public.mandatoryReview).toEqual(false); + expect(response.body.datas.reviews.public.minReviewers).toEqual(5); + expect(response.body.datas.report.private.imageBorderColor).toEqual( + '#123456', + ); + }); + + it('Revert settings', async () => { + var response = await request(app) + .put('/api/settings/revert') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + + var response = await request(app) + .get('/api/settings') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + expect(response.body.datas).toEqual(defaultSettings); + }); + + it('Export settings', async () => { + var response = await request(app) + .get('/api/settings/export') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + expect(response.type).toEqual('application/json'); + expect( + response.headers['content-disposition'].indexOf( + 'attachment; filename=', + ), + ).toBe(0); + }); + }); +}; diff --git a/backend/tests/template.test.js b/backend/tests/template.test.js new file mode 100644 index 0000000000000000000000000000000000000000..5ade48b244421dfc2ffd060edb19d62df2efc965 --- /dev/null +++ b/backend/tests/template.test.js @@ -0,0 +1,148 @@ +/* + At the end + 1 Template: {name: 'Test Template 2', file: 'VGVzdCB0ZW1wbGF0ZQ=='} +*/ + +module.exports = function (request, app) { + describe('Template Suite Tests', () => { + var userToken = ''; + beforeAll(async () => { + var response = await request(app) + .post('/api/users/token') + .send({ username: 'admin', password: 'Admin123' }); + userToken = response.body.datas.token; + }); + + describe('Template CRUD operations', () => { + var template1Id = ''; + var template2Id = ''; + it('Get templates (no existing templates in db)', async () => { + var response = await request(app) + .get('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(0); + }); + + it('Create 2 templates', async () => { + var template = { + name: 'Template 1', + ext: 'docx', + file: 'VGVzdCB0ZW1wbGF0ZQ==', + }; + var response = await request(app) + .post('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(template); + + expect(response.status).toBe(201); + template1Id = response.body.datas._id; + + template.name = 'Template 2'; + var response = await request(app) + .post('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(template); + expect(response.status).toBe(201); + template2Id = response.body.datas._id; + }); + + it('Should not create template with name without file', async () => { + var template = { name: 'Template 3' }; + var response = await request(app) + .post('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(template); + + expect(response.status).toBe(422); + }); + + it('Should not create template with existing name', async () => { + var template = { + name: 'Template 1', + filename: 'File.docx', + file: 'VGVzdCB0ZW1wbGF0ZQ==', + }; + var response = await request(app) + .post('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(template); + + expect(response.status).toBe(422); + }); + + it('Should not create template with wrong name format', async () => { + var template = { + name: '../../../../../../etc/passwd', + file: 'VGVzdCB0ZW1wbGF0ZQ==', + }; + var response = await request(app) + .post('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(template); + expect(response.status).toBe(422); + + template.name = '%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd'; + response = await request(app) + .post('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(template); + expect(response.status).toBe(422); + }); + + it('Get templates (existing templates in db)', async () => { + const expected = [{ name: 'Template 1' }, { name: 'Template 2' }]; + var response = await request(app) + .get('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect( + response.body.datas.map(t => { + return { name: t.name }; + }), + ).toEqual(expect.arrayContaining(expected)); + }); + + it('Update template with name only', async () => { + var template = { name: 'Template Updated' }; + + var response = await request(app) + .put(`/api/templates/${template1Id}`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send(template); + expect(response.status).toBe(200); + }); + + it('Update template with nonexistent ID', async () => { + var template = { name: 'Template Updated' }; + + var response = await request(app) + .put(`/api/templates/deadbeefdeadbeefdeadbeef`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send(template); + expect(response.status).toBe(404); + }); + + it('Delete template', async () => { + var response = await request(app) + .delete(`/api/templates/${template2Id}`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + + response = await request(app) + .get('/api/templates') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(1); + }); + + it('Delete template with nonexistent ID', async () => { + var response = await request(app) + .delete(`/api/templates/deadbeefdeadbeefdeadbeef`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(404); + }); + }); + }); +}; diff --git a/backend/tests/unauthenticated.test.js b/backend/tests/unauthenticated.test.js new file mode 100644 index 0000000000000000000000000000000000000000..c1960459f307028427bd9b4f3f4a5f8f4dc57bbc --- /dev/null +++ b/backend/tests/unauthenticated.test.js @@ -0,0 +1,348 @@ +module.exports = function (request, app) { + describe('Unhauthenticated Suite Tests', () => { + describe('Testing Unauthenticated User routes', () => { + it('should return 401 Unauthorized', async () => { + var response = await request(app).get('/api/users'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/users'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/users/checktoken'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/users/me'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/users/me'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/users/refreshtoken'); + expect(response.status).toBe(401); + + response = await request(app).delete('/api/users/refreshtoken'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/users/reviewers'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/users/token'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/users/totp'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/users/totp'); + expect(response.status).toBe(401); + + response = await request(app).delete('/api/users/totp'); + expect(response.status).toBe(401); + + // put /api/users/{id} + response = await request(app).put('/api/users/1'); + expect(response.status).toBe(401); + + // get /api/users/{username} + response = await request(app).get('/api/users/test'); + expect(response.status).toBe(401); + }); + + it('should return 200 Ok', async () => { + // First use + var response = await request(app).get('/api/users/init'); + expect(response.status).toBe(200); + + var response = await request(app).post('/api/users/init'); + expect(response.status).toBe(422); + + // Login + response = await request(app).post('/api/users/token'); + expect(response.status).toBe(422); + }); + }); + + describe('Testing Unauthenticated Data routes', () => { + it('should return 401 Unauthorized', async () => { + var response = await request(app).get('/api/data/roles'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/data/languages'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/data/languages'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/data/languages'); + expect(response.status).toBe(401); + + // delete /api/data/languages/{locale(*)} + response = await request(app).delete('/api/data/languages/en'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/data/audit-types'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/data/audit-types'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/data/audit-types'); + expect(response.status).toBe(401); + + // delete /api/data/audit-types/{name(*)} + response = await request(app).delete('/api/data/audit-types/Internal'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/data/vulnerability-categories'); + expect(response.status).toBe(401); + + response = await request(app).post( + '/api/data/vulnerability-categories', + ); + expect(response.status).toBe(401); + + response = await request(app).put('/api/data/vulnerability-categories'); + expect(response.status).toBe(401); + + // delete /api/data/vulnerability-categories/{name(*)} + response = await request(app).delete( + '/api/data/vulnerability-categories/Web', + ); + expect(response.status).toBe(401); + + response = await request(app).get('/api/data/sections'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/data/sections'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/data/sections'); + expect(response.status).toBe(401); + + // delete /api/data/sections/{field}/{locale(*)} + response = await request(app).delete( + '/api/data/sections/attack_scenario/en', + ); + expect(response.status).toBe(401); + + response = await request(app).get('/api/data/custom-fields'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/data/custom-fields'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/data/custom-fields'); + expect(response.status).toBe(401); + + // delete /api/data/custom-fields/{fieldId} + response = await request(app).delete( + '/api/data/custom-fields/attack_scenario', + ); + expect(response.status).toBe(401); + }); + }); + + describe('Testing Unauthenticated Company routes', () => { + it('should return 401 Unauthorized', async () => { + var response = await request(app).get('/api/companies'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/companies'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/companies/FSociety'); + expect(response.status).toBe(401); + + response = await request(app).delete('/api/companies/FSociety'); + expect(response.status).toBe(401); + }); + }); + + describe('Testing Unauthenticated Client routes', () => { + it('should return 401 Unauthorized', async () => { + var response = await request(app).get('/api/clients'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/clients'); + expect(response.status).toBe(401); + + // put /api/clients/{id} + response = await request(app).put('/api/clients/test@example.com'); + expect(response.status).toBe(401); + + response = await request(app).delete('/api/clients/test@example.com'); + expect(response.status).toBe(401); + }); + }); + + describe('Testing Unauthenticated Template routes', () => { + it('should return 401 Unauthorized', async () => { + var response = await request(app).get('/api/templates'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/templates'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/templates/1234'); + expect(response.status).toBe(401); + + response = await request(app).delete('/api/templates/1234'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/templates/download/1234'); + expect(response.status).toBe(401); + }); + }); + + describe('Testing Unauthenticated Images routes', () => { + it('should return 401 Unauthorized', async () => { + // get /api/images/{imageId} + var response = await request(app).get('/api/images/1'); + expect(response.status).toBe(401); + + // delete /api/images/{imageId} + response = await request(app).delete('/api/images/1'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/images'); + expect(response.status).toBe(401); + + // get /api/images/download/{imageId} + response = await request(app).get('/api/images/download/1'); + expect(response.status).toBe(401); + }); + }); + + describe('Testing Unauthenticated Settings routes', () => { + it('should return 401 Unauthorized', async () => { + var response = await request(app).get('/api/settings'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/settings'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/settings/public'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/settings/revert'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/settings/export'); + expect(response.status).toBe(401); + }); + }); + + describe('Testing Unauthenticated Vulnerability routes', () => { + it('should return 401 Unauthorized', async () => { + var response = await request(app).get('/api/vulnerabilities'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/vulnerabilities'); + expect(response.status).toBe(401); + + response = await request(app).delete('/api/vulnerabilities'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/vulnerabilities/export'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/vulnerabilities/1234'); + expect(response.status).toBe(401); + + response = await request(app).delete('/api/vulnerabilities/1234'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/vulnerabilities/en'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/vulnerabilities/finding/en'); + expect(response.status).toBe(401); + + response = await request(app).get('/api/vulnerabilities/updates/1234'); + expect(response.status).toBe(401); + + response = await request(app).put('/api/vulnerabilities/merge/1234'); + expect(response.status).toBe(401); + }); + }); + + describe('Testing Unauthenticated Audit routes', () => { + it('should return 401 Unauthorized', async () => { + var response = await request(app).get('/api/audits'); + expect(response.status).toBe(401); + + response = await request(app).post('/api/audits'); + expect(response.status).toBe(401); + + // delete /api/audits/{auditId} + response = await request(app).delete('/api/audits/1234'); + expect(response.status).toBe(401); + + // get /api/audits/{auditId} + response = await request(app).get('/api/audits/1234'); + expect(response.status).toBe(401); + + // get /api/audits/{auditId}/general + response = await request(app).get('/api/audits/1234/general'); + expect(response.status).toBe(401); + + // put /api/audits/{auditId}/general + response = await request(app).put('/api/audits/1234/general'); + expect(response.status).toBe(401); + + // get /api/audits/{auditId}/network + response = await request(app).get('/api/audits/1234/network'); + expect(response.status).toBe(401); + + // put /api/audits/{auditId}/network + response = await request(app).put('/api/audits/1234/network'); + expect(response.status).toBe(401); + + // post /api/audits/{auditId}/findings + response = await request(app).post('/api/audits/1234/findings'); + expect(response.status).toBe(401); + + // get /api/audits/{auditId}/findings/{findingId} + response = await request(app).get('/api/audits/1234/findings/1234'); + expect(response.status).toBe(401); + + // put /api/audits/{auditId}/findings/{findingId} + response = await request(app).put('/api/audits/1234/findings/1234'); + expect(response.status).toBe(401); + + // delete /api/audits/{auditId}/findings/{findingId} + response = await request(app).delete('/api/audits/1234/findings/1234'); + expect(response.status).toBe(401); + + // get /api/audits/{auditId}/sections/{sectionId} + response = await request(app).get('/api/audits/1234/sections/1234'); + expect(response.status).toBe(401); + + // put /api/audits/{auditId}/sections/{sectionId} + response = await request(app).put('/api/audits/1234/sections/1234'); + expect(response.status).toBe(401); + + // get /api/audits/{auditId}/generate + response = await request(app).get('/api/audits/1234/generate'); + expect(response.status).toBe(401); + + // put /api/audits/{auditId}/sortfindings + response = await request(app).put('/api/audits/1234/sortfindings'); + expect(response.status).toBe(401); + + // put /api/audits/{auditId}/movefinding + response = await request(app).put('/api/audits/1234/movefinding'); + expect(response.status).toBe(401); + + // put /api/audits/{auditId}/toggleApproval + response = await request(app).put('/api/audits/1234/toggleApproval'); + expect(response.status).toBe(401); + + // put /api/audits/{auditId}/updateReadyForReview + response = await request(app).put( + '/api/audits/1234/updateReadyForReview', + ); + expect(response.status).toBe(401); + }); + }); + }); +}; diff --git a/backend/tests/user.test.js b/backend/tests/user.test.js new file mode 100644 index 0000000000000000000000000000000000000000..05889223225ccdb0c269a3a8e7ae26625304e3d6 --- /dev/null +++ b/backend/tests/user.test.js @@ -0,0 +1,298 @@ +/* + 4 Users at the end: + admin:Admin123 (admin) + user2:User1234 (user) + report:Report123 (report) + reviewer:Reviewer123 (reviewer) +*/ + +module.exports = function (request, app) { + var userToken = ''; + + describe('Users Suite Tests', () => { + describe('User Initialization', () => { + it('Get the users init state', async () => { + var response = await request(app).get('/api/users/init'); + + expect(response.status).toBe(200); + expect(response.body.datas).toBe(true); + }); + + it('Authenticate with nonexistent user', async () => { + var response = await request(app) + .post('/api/users/token') + .send({ username: 'admin', password: 'Admin123' }); + + expect(response.status).toBe(401); + }); + + it('Create first user', async () => { + var user = { + username: 'admin', + password: 'Password1', + firstname: 'Admin', + lastname: 'Istrator', + }; + var response = await request(app).post('/api/users/init').send(user); + + expect(response.status).toBe(201); + }); + + it('Create first user when it already exists', async () => { + var user = { + username: 'admin2', + password: 'Admin123', + firstname: 'Admin2', + lastname: 'Istrator2', + }; + var response = await request(app).post('/api/users/init').send(user); + + expect(response.status).toBe(403); + }); + + it('Authenticate with first user', async () => { + var user = { + username: 'admin', + password: 'Password1', + }; + var response = await request(app).post('/api/users/token').send(user); + expect(response.status).toBe(200); + expect(response.body.datas.token).toBeDefined(); + expect(response.body.datas.token).toContain('eyJ'); + + userToken = response.body.datas.token; + }); + }); + + describe('User CRUD operations', () => { + it('Check token validity', async () => { + var response = await request(app) + .get('/api/users/checktoken') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + }); + + it('Get my profile', async () => { + const expected = { + username: 'admin', + firstname: 'Admin', + lastname: 'Istrator', + role: 'admin', + }; + var response = await request(app) + .get('/api/users/me') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toEqual(expect.objectContaining(expected)); + }); + + it('Create user with role user', async () => { + var user = { + username: 'user', + password: 'Password1', + firstname: 'User', + lastname: 'Test', + role: 'user', + }; + var response = await request(app) + .post('/api/users') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(user); + + expect(response.status).toBe(201); + + response = await request(app).post('/api/users/token').send(user); + expect(response.status).toBe(200); + }); + + it('Create user with role user without role parameter', async () => { + var user = { + username: 'tmpuser', + password: 'Tmpuser1', + firstname: 'Tmp', + lastname: 'User', + }; + var response = await request(app) + .post('/api/users') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(user); + + expect(response.status).toBe(201); + + response = await request(app).post('/api/users/token').send(user); + expect(response.status).toBe(200); + }); + + it('Create user with role report', async () => { + var user = { + username: 'report', + password: 'Report123', + firstname: 'Report', + lastname: 'Admin', + role: 'report', + }; + var response = await request(app) + .post('/api/users') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(user); + + expect(response.status).toBe(201); + + response = await request(app).post('/api/users/token').send(user); + expect(response.status).toBe(200); + }); + + it('Create user with role reviewer', async () => { + var user = { + username: 'reviewer', + password: 'Reviewer123', + firstname: 'reviewer', + lastname: 'reviewer', + role: 'reviewer', + }; + var response = await request(app) + .post('/api/users') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(user); + + expect(response.status).toBe(201); + + response = await request(app).post('/api/users/token').send(user); + expect(response.status).toBe(200); + }); + + it('Get user profile', async () => { + const expected = { + username: 'user', + firstname: 'User', + lastname: 'Test', + role: 'user', + }; + var response = await request(app) + .get('/api/users/user') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toEqual(expect.objectContaining(expected)); + }); + + it('Update my profile', async () => { + const expected = { + username: 'admin', + firstname: 'Admin2', + lastname: 'Istrator', + role: 'admin', + }; + + var user = { + currentPassword: 'Password1', + newPassword: 'Admin123', + confirmPassword: 'Admin123', + firstname: 'Admin2', + }; + var response = await request(app) + .put('/api/users/me') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(user); + + expect(response.status).toBe(200); + + var response = await request(app) + .get('/api/users/me') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.body.datas).toEqual(expect.objectContaining(expected)); + }); + + it('Update user profile', async () => { + const expected = { + username: 'user2', + firstname: 'User2', + lastname: 'Test', + role: 'user', + }; + + var user = { + username: 'user2', + firstname: 'User2', + password: 'User1234', + }; + + var userRequest = await request(app) + .get('/api/users/user') + .set('Cookie', [`token=JWT ${userToken}`]); + + var userId = userRequest.body.datas._id; + + var response = await request(app) + .put(`/api/users/${userId}`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send(user); + + expect(response.status).toBe(200); + + var response = await request(app) + .get('/api/users/user2') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.body.datas).toEqual(expect.objectContaining(expected)); + }); + }); + + describe('User Enumeration testing', () => { + it('User Enumeration due to Response Discrepancy', async () => { + var validUserBadPassword = { + username: 'admin', + password: 'InvalidPassword', + }; + var invalidUser = { + username: 'InvalidUser', + password: 'InvalidPassword', + }; + + var responseValidBadPassword = await request(app) + .post('/api/users/token') + .send(validUserBadPassword); + expect(responseValidBadPassword.status).toBe(401); + + var responseInvalid = await request(app) + .post('/api/users/token') + .send(invalidUser); + expect(responseInvalid.status).toBe(401); + + expect(responseValidBadPassword.text).toBe(responseInvalid.text); + }); + + it('User Enumeration based on response time', async () => { + var start = new Date(); + + for (var index = 0; index <= 200; index++) { + await request(app) + .post('/api/users/token') + .send({ + username: 'InvalidUser' + index, + password: 'InvalidPassword', + }); + } + + var endTimeInvalidUsers = new Date() - start; + + start = new Date(); + + for (var index = 0; index <= 200; index++) { + await request(app) + .post('/api/users/token') + .send({ username: 'admin', password: 'InvalidPassword' + index }); + } + + var endTimeValidUsers = new Date() - start; + + var timeDelta = endTimeInvalidUsers / endTimeValidUsers; + + expect(timeDelta <= 1.15 && timeDelta >= 0.85).toBeTruthy(); + }, 50000); + }); + }); +}; diff --git a/backend/tests/vulnerability.test.js b/backend/tests/vulnerability.test.js new file mode 100644 index 0000000000000000000000000000000000000000..170e85738ec80c5e3e403361f7710b43408cbfec --- /dev/null +++ b/backend/tests/vulnerability.test.js @@ -0,0 +1,475 @@ +/* + At the end + 3 Vulnerabilities: [ + { + status: 2 + details: [ + {locale: 'en', title: 'Vulnerability English 1', vulnType: 'Internal'}, + { + locale: 'fr', + title: 'Vulnerability French 1', + vulnType: 'Internal', + description: 'French vuln description', + observation: 'French vuln observation', + remediation: 'French vuln remediation' + } + ] + }, + { + cvssv3: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H", + status: 0 + details: [ + {locale: 'en', title: 'Vulnerability English 2', vulnType: 'Internal'}, + { + locale: 'fr', + title: 'Vulnerability French 2', + vulnType: 'Internal', + description: 'French vuln description', + observation: 'French vuln observation', + remediation: 'French vuln remediation', + references: ['Ref1', 'Ref2'] + } + ] + }, + { + status: 1, + details: [ + {locale: 'en', title: 'New vulnerability from finding', vulnType: 'Internal', description: 'New vuln description'} + ] + } + ] + +*/ + +module.exports = function (request, app) { + describe('Vulnerability Suite Tests', () => { + var userToken = ''; + beforeAll(async () => { + var response = await request(app) + .post('/api/users/token') + .send({ username: 'admin', password: 'Admin123' }); + userToken = response.body.datas.token; + }); + + describe('Vulnerability CRUD operations', () => { + it('Get vulnerabilities (no existing vulnerabilities in db)', async () => { + var response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(0); + }); + + it('Get vulnerabilities for export (no existing vulnerabilities in db)', async () => { + var response = await request(app) + .get('/api/vulnerabilities/export') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.type).toEqual('application/json'); + //expect(response.headers['content-disposition'].indexOf('attachment; filename=')).toBe(0); + + expect(response.body.datas).toEqual([]); + }); + + it('Create 4 vulnerabilities', async () => { + var vuln1 = { + details: [ + { + locale: 'en', + title: 'Vulnerability English 1', + vulnType: 'Internal', + }, + ], + }; + + var vuln2 = { + details: [ + { + locale: 'en', + title: 'Vulnerability English 2', + vulnType: 'Internal', + }, + ], + }; + + var vuln3 = { + details: [ + { + locale: 'es', + title: 'Vulnerability Espagnol 1', + vulnType: 'Web', + }, + { locale: 'es', vulnType: 'Web' }, + { title: 'Vulnerability Espagnol 2', vulnType: 'Web' }, + ], + }; + + var vuln4 = { + cvssv3: + 'CVSS3.0:/AV:A/AC:H/PR:N/UI:N/S:C/C:L/I:L/A:L/E:X/RL:X/RC:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MPR:X/MUI:X/MS:X/MC:X/MI:X/MA:X', + status: 1, + details: [ + { + locale: 'fr', + title: 'Vulnerability French 1', + vulnType: 'Internal', + description: 'French vuln description', + observation: 'French vuln observation', + remediation: 'French vuln remediation', + references: ['Reference 1', 'Reference 2'], + }, + ], + }; + + var response = await request(app) + .post('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]) + .send([vuln1, vuln2, vuln3, vuln4]); + expect(response.status).toBe(201); + }); + + it('Should not create vulnerability with with existing title', async () => { + var vuln1 = { + details: [ + { + locale: 'fr', + title: 'Vulnerability English 1', + vulnType: 'Internal', + }, + ], + }; + + var response = await request(app) + .post('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]) + .send([vuln1]); + expect(response.status).toBe(422); + }); + + it('Should not create vulnerability without title', async () => { + var vuln1 = { + details: [{ locale: 'fr', vulnType: 'Internal' }], + }; + + var response = await request(app) + .post('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]) + .send([vuln1]); + + expect(response.status).toBe(422); + }); + + it('Should not create vulnerability without locale', async () => { + var vuln1 = { + details: [{ title: 'Vulnerability English', vulnType: 'Internal' }], + }; + + var response = await request(app) + .post('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]) + .send([vuln1]); + + expect(response.status).toBe(422); + }); + + it('Get vulnerabilities (existing vulnerabilities in db)', async () => { + var response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + + expect(response.body.datas[0].details[0].locale).toEqual('en'); + expect(response.body.datas[0].details[0].title).toEqual( + 'Vulnerability English 1', + ); + expect(response.body.datas[0].details[0].vulnType).toEqual('Internal'); + + expect(response.body.datas[1].details[0].locale).toEqual('en'); + expect(response.body.datas[1].details[0].title).toEqual( + 'Vulnerability English 2', + ); + expect(response.body.datas[1].details[0].vulnType).toEqual('Internal'); + + expect(response.body.datas[2].details[0].locale).toEqual('es'); + expect(response.body.datas[2].details[0].title).toEqual( + 'Vulnerability Espagnol 1', + ); + expect(response.body.datas[2].details[0].vulnType).toEqual('Web'); + + expect(response.body.datas[3].cvssv3).toEqual( + 'CVSS3.0:/AV:A/AC:H/PR:N/UI:N/S:C/C:L/I:L/A:L/E:X/RL:X/RC:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MPR:X/MUI:X/MS:X/MC:X/MI:X/MA:X', + ); + expect(response.body.datas[3].details[0].locale).toEqual('fr'); + expect(response.body.datas[3].details[0].title).toEqual( + 'Vulnerability French 1', + ); + expect(response.body.datas[3].details[0].vulnType).toEqual('Internal'); + expect(response.body.datas[3].details[0].description).toEqual( + 'French vuln description', + ); + expect(response.body.datas[3].details[0].observation).toEqual( + 'French vuln observation', + ); + expect(response.body.datas[3].details[0].remediation).toEqual( + 'French vuln remediation', + ); + expect(response.body.datas[3].details[0].references).toEqual([ + 'Reference 1', + 'Reference 2', + ]); + }); + + it('Get vulnerabilities for export (existing vulnerabilities in db)', async () => { + var response = await request(app) + .get('/api/vulnerabilities/export') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.status).toBe(200); + expect(response.type).toEqual('application/json'); + //expect(response.headers['content-disposition'].indexOf('attachment; filename=')).toBe(0); + + expect(response.body.datas[0].details[0].locale).toEqual('en'); + expect(response.body.datas[0].details[0].title).toEqual( + 'Vulnerability English 1', + ); + expect(response.body.datas[0].details[0].vulnType).toEqual('Internal'); + + expect(response.body.datas[1].details[0].locale).toEqual('en'); + expect(response.body.datas[1].details[0].title).toEqual( + 'Vulnerability English 2', + ); + expect(response.body.datas[1].details[0].vulnType).toEqual('Internal'); + + expect(response.body.datas[2].details[0].locale).toEqual('es'); + expect(response.body.datas[2].details[0].title).toEqual( + 'Vulnerability Espagnol 1', + ); + expect(response.body.datas[2].details[0].vulnType).toEqual('Web'); + + expect(response.body.datas[3].cvssv3).toEqual( + 'CVSS3.0:/AV:A/AC:H/PR:N/UI:N/S:C/C:L/I:L/A:L/E:X/RL:X/RC:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MPR:X/MUI:X/MS:X/MC:X/MI:X/MA:X', + ); + expect(response.body.datas[3].details[0].locale).toEqual('fr'); + expect(response.body.datas[3].details[0].title).toEqual( + 'Vulnerability French 1', + ); + expect(response.body.datas[3].details[0].vulnType).toEqual('Internal'); + expect(response.body.datas[3].details[0].description).toEqual( + 'French vuln description', + ); + expect(response.body.datas[3].details[0].observation).toEqual( + 'French vuln observation', + ); + expect(response.body.datas[3].details[0].remediation).toEqual( + 'French vuln remediation', + ); + expect(response.body.datas[3].details[0].references).toEqual([ + 'Reference 1', + 'Reference 2', + ]); + }); + + it('Update vulnerability', async () => { + var update = { + cvssv3: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H', + details: [ + { + locale: 'en', + title: 'Vulnerability English 2', + vulnType: 'Internal', + }, + { + locale: 'fr', + title: 'Vulnerability French 2', + vulnType: 'Internal', + description: 'French vuln description', + observation: 'French vuln observation', + remediation: 'French vuln remediation', + references: ['Ref1', 'Ref2'], + }, + ], + }; + + var response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + var vulnId = response.body.datas[1]._id; + response = await request(app) + .put(`/api/vulnerabilities/${vulnId}`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send(update); + expect(response.status).toBe(200); + + response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + + expect(response.body.datas[1].cvssv3).toEqual( + 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H', + ); + expect(response.body.datas[1].details[0].locale).toEqual('en'); + expect(response.body.datas[1].details[0].title).toEqual( + 'Vulnerability English 2', + ); + expect(response.body.datas[1].details[0].vulnType).toEqual('Internal'); + expect(response.body.datas[1].details[1].locale).toEqual('fr'); + expect(response.body.datas[1].details[1].title).toEqual( + 'Vulnerability French 2', + ); + expect(response.body.datas[1].details[1].vulnType).toEqual('Internal'); + expect(response.body.datas[1].details[1].description).toEqual( + 'French vuln description', + ); + expect(response.body.datas[1].details[1].observation).toEqual( + 'French vuln observation', + ); + expect(response.body.datas[1].details[1].remediation).toEqual( + 'French vuln remediation', + ); + expect(response.body.datas[1].details[1].references).toEqual([ + 'Ref1', + 'Ref2', + ]); + }); + + it('Should not update vulnerability with nonexistent ID', async () => { + var vulnerability = { + details: [{ locale: 'en', title: 'Vulnerability English' }], + }; + + var response = await request(app) + .put(`/api/vulnerabilities/deadbeefdeadbeefdeadbeef`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send(vulnerability); + expect(response.status).toBe(404); + }); + + it('Get vulnerabilities by language', async () => { + var response = await request(app) + .get(`/api/vulnerabilities/en`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(2); + }); + + it('Merge 2 vulnerabilities', async () => { + var response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + var vulnId = response.body.datas[0]._id; + var vulnIdMerge = response.body.datas[3]._id; + + response = await request(app) + .put(`/api/vulnerabilities/merge/${vulnId}`) + .set('Cookie', [`token=JWT ${userToken}`]) + .send({ vulnId: vulnIdMerge, locale: 'fr' }); + expect(response.status).toBe(200); + + response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(3); + }); + + it('Delete vulnerability', async () => { + var response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + var vulnId = response.body.datas[2]._id; + response = await request(app) + .delete(`/api/vulnerabilities/${vulnId}`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + + response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(2); + }); + + it('Delete vulnerability with nonexistent ID', async () => { + var response = await request(app) + .delete(`/api/vulnerabilities/deadbeefdeadbeefdeadbeef`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(404); + }); + + it('Create vulnerability from finding', async () => { + var finding = { + title: 'New vulnerability from finding', + vulnType: 'Internal', + description: 'New vuln description', + }; + var response = await request(app) + .post('/api/vulnerabilities/finding/en') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(finding); + expect(response.status).toBe(201); + + response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(3); + expect(response.body.datas[2].status).toBe(1); + }); + + it('Update vulnerability from finding', async () => { + var finding = { + title: 'Vulnerability English 1', + description: 'Description English 1', + observation: 'Observation English 1', + }; + var response = await request(app) + .post('/api/vulnerabilities/finding/en') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(finding); + expect(response.status).toBe(200); + + response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.body.datas).toHaveLength(3); + expect(response.body.datas[0].status).toBe(2); + }); + + it('Should not create/update vulnerability from finding without title', async () => { + var finding = { + observation: 'Observation new vuln from finding', + }; + var response = await request(app) + .post('/api/vulnerabilities/finding/en') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(finding); + expect(response.status).toBe(422); + }); + + it('Should not update vulnerability from finding that is not yet approved', async () => { + var finding = { + title: 'New vulnerability from finding', + observation: 'Observation new vuln from finding', + }; + var response = await request(app) + .post('/api/vulnerabilities/finding/en') + .set('Cookie', [`token=JWT ${userToken}`]) + .send(finding); + expect(response.status).toBe(403); + }); + + it('Get vulnerability updates', async () => { + var response = await request(app) + .get('/api/vulnerabilities') + .set('Cookie', [`token=JWT ${userToken}`]); + var vulnId = response.body.datas[0]._id; + + response = await request(app) + .get(`/api/vulnerabilities/updates/${vulnId}`) + .set('Cookie', [`token=JWT ${userToken}`]); + expect(response.status).toBe(200); + expect(response.body.datas).toHaveLength(1); + }); + }); + }); +}; diff --git a/cwe_api/Dockerfile b/cwe_api/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..aef52aef15809bc671282b44c32882d5cc39f747 --- /dev/null +++ b/cwe_api/Dockerfile @@ -0,0 +1,41 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY . . +ARG CWE_MODEL_URL +ARG CVSS_MODEL_URL + +RUN set -e \ + && apt-get update -y \ + && apt-get install -y --no-install-recommends \ + wget \ + unzip \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + || { echo "Error: Fallo en la instalación de paquetes"; exit 1; } + +RUN pip install --no-cache-dir torchvision==0.17.0+cpu torchaudio==2.2.0+cpu --index-url https://download.pytorch.org/whl/cpu \ + && pip install --no-cache-dir -r requirements.txt + +RUN if [ ! -d modelo_cwe ]; then \ + echo "modelo_cwe not found. Downloading it..."; \ + wget --progress=bar:force "$CWE_MODEL_URL" -O modelo_cwe.zip \ + && unzip modelo_cwe.zip \ + || { echo "Failed to download or unzip model"; exit 1; } \ + fi + +RUN if [ ! -d utils ]; then \ + echo "utils folder (cvss model) not found. Downloading it..."; \ + if wget --progress=bar:force "$CVSS_MODEL_URL" -O modelo_cvss.zip && \ + unzip modelo_cvss.zip && rm modelo_cvss.zip; then \ + echo "CVSS model downloaded and extracted successfully"; \ + else \ + echo "Failed to download or unzip CVSS model"; \ + exit 1; \ + fi; \ +fi + +EXPOSE 8000 + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--ssl-keyfile", "ssl/key.pem", "--ssl-certfile", "ssl/cert.pem"] diff --git a/cwe_api/cvss_inferencer.py b/cwe_api/cvss_inferencer.py new file mode 100644 index 0000000000000000000000000000000000000000..24b8f685a5d24a2efc6d791f85898336359f93a9 --- /dev/null +++ b/cwe_api/cvss_inferencer.py @@ -0,0 +1,37 @@ +import joblib + +def generate_cvss_string(output_decoded): + + output_decoded = output_decoded[:-1] + + pair_name2letter = {'AV': {'NETWORK': 'N', 'ADJACENT': 'A', 'LOCAL': 'L', 'PHYSICAL': 'P'}, + 'AC': {'LOW': 'L', 'HIGH': 'H'}, + 'PR': {'NONE': 'N', 'LOW': 'L', 'HIGH': 'H'}, + 'UI': {'NONE': 'N', 'REQUIRED': 'R'}, + 'S': {'UNCHANGED': 'U', 'CHANGED': 'C'}, + 'C': {'NONE': 'N', 'LOW':'L', 'HIGH': 'H'}, + 'I': {'NONE': 'N', 'LOW': 'L', 'HIGH':'H'}, + 'A': {'NONE':'N', 'LOW':'L', 'HIGH':'H'}} + + cvss_string = 'CVSS:3.1/' + pair_name = list(pair_name2letter.keys()) + + for index, output in enumerate(output_decoded): + + cvss_string += pair_name[index] + ':' + pair_name2letter[pair_name[index]][output] + + if index != 7: + cvss_string += '/' + + return cvss_string + +def inferencer(vuln): + clf = joblib.load("model.pkl") + output = clf.predict([vuln]) + output = generate_cvss_string(output_decoded=output[0]) + return output + +if __name__ == '__main__': + + vuln = 'Ciertos servidores NFS permiten a los usuarios usar mknod para obtener privilegios creando un dispositivo de kmem escrito y configurando el UID a 0.' + print(inferencer(vuln)) \ No newline at end of file diff --git a/cwe_api/inferencer.py b/cwe_api/inferencer.py new file mode 100644 index 0000000000000000000000000000000000000000..df497b81cdc7f9721dbfb76e0c7ee2dcde129e65 --- /dev/null +++ b/cwe_api/inferencer.py @@ -0,0 +1,44 @@ +from transformers import TextClassificationPipeline +from transformers import AutoTokenizer +from transformers import AutoModelForSequenceClassification +from torch import nn +import json +import numpy as np + +ID2LABEL_PATH = './id2label.json' +LABEL2ID_PATH = './label2id.json' +MODEL_PATH = "./modelo_cwe/checkpoint-141693" +NUMBER_OF_PREDICTIONS = 3 + +class BestCweClassifications(TextClassificationPipeline): + def postprocess(self, model_outputs): + best_class = model_outputs["logits"] + return best_class + +def inferencer(vuln): + + with open(ID2LABEL_PATH) as f: + id2label = json.load(f) + + with open(LABEL2ID_PATH) as f: + label2id = json.load(f) + + tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-multilingual-cased") + model = AutoModelForSequenceClassification.from_pretrained(MODEL_PATH, + num_labels=len(label2id), + id2label=id2label, + label2id=label2id) + + m = nn.Softmax(dim=1) + + pipe = BestCweClassifications(model=model, tokenizer=tokenizer) + output = pipe(vuln, batch_size=2, truncation="only_first") + + softmax_output = m(output[0])[0] + ind = np.argpartition(softmax_output, -NUMBER_OF_PREDICTIONS)[-NUMBER_OF_PREDICTIONS:] + + reversed_indices = np.flip(ind.numpy(),0).copy() + score = softmax_output[reversed_indices] + + return [{'priority': i, 'label': id2label[str(reversed_indices[i])], 'score': float(score[i].numpy())} for i in range(0, 3)] + diff --git a/cwe_api/main.py b/cwe_api/main.py new file mode 100644 index 0000000000000000000000000000000000000000..65340d5014d8b93a139823eae414718f8beb722f --- /dev/null +++ b/cwe_api/main.py @@ -0,0 +1,131 @@ +import ssl +import hashlib +import requests +import os +import shutil +import zipfile +from pathlib import Path +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel +from fastapi.middleware.cors import CORSMiddleware +from transformers import pipeline +from inferencer import inferencer +from cvss_inferencer import inferencer as cvss_inferencer + +app = FastAPI() + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Permitir solicitudes desde cualquier origen + allow_credentials=True, + allow_methods=["GET", "POST", "PUT", "DELETE"], # Métodos HTTP permitidos + allow_headers=["*"], # Cabeceras permitidas +) + +LOCAL_FILE_PATH = Path("/app/modelo_cwe.zip") +REMOTE_FILE_URL = "https://drive.usercontent.google.com/download?id=1OtRNObv-Il2B5nDnpzMSGj_yBJAlskuS&export=download&confirm=" +CWE_MODEL_FOLDER_PATH = Path("/app/modelo_cwe") +APP_PATH = Path("/app") + +BYTE_RANGE = (0, 10485759) # 10 MB + +def calculate_local_checksum(file_path, byte_limit): + sha256_hash = hashlib.sha256() + with open(file_path, "rb") as f: + chunk = f.read(byte_limit) + sha256_hash.update(chunk) + return sha256_hash.hexdigest() + +def calculate_remote_checksum(url, byte_range): + headers = {'Range': f'bytes={byte_range[0]}-{byte_range[1]}'} + try: + response = requests.get(url, headers=headers, stream=True) + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise Exception(f"Error al obtener el archivo remoto: {str(e)}") from e + + sha256_hash = hashlib.sha256() + for chunk in response.iter_content(chunk_size=8192): + sha256_hash.update(chunk) + return sha256_hash.hexdigest() +class VulnerabilityRequest(BaseModel): + vuln: str + +@app.post("/classify") +async def classify_vulnerability(vuln_request: VulnerabilityRequest): + vuln = vuln_request.vuln + result = inferencer(vuln) + return {"result": result} + +@app.post("/cvss") +async def classify_vulnerability(vuln_request: VulnerabilityRequest): + vuln = vuln_request.vuln + result = cvss_inferencer(vuln) + return {"result": result} + +@app.get("/") +async def read_root(): + example_vuln = ( + "Los dispositivos de CPU Siemens SIMATIC S7-300 permiten a los atacantes remotos causar una denegación de servicio " + "(transición de modo de defecto) a través de paquetes elaborados en (1) puerto TCP 102 o (2) Profibus." + ) + result = inferencer(example_vuln) + return {"example_vuln": example_vuln, "result": result} + +@app.get("/check_cwe_update") +async def check_cwe_update(): + try: + local_checksum = calculate_local_checksum(LOCAL_FILE_PATH, byte_limit=BYTE_RANGE[1] + 1) + remote_checksum = calculate_remote_checksum(REMOTE_FILE_URL, BYTE_RANGE) + + match = local_checksum == remote_checksum + return {"checksum_match": match} + + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) from e + +@app.post("/update_cwe_model") +async def update_cwe_model(): + backup_file_path = f"{LOCAL_FILE_PATH}.bak" + try: + if os.path.exists(LOCAL_FILE_PATH): + if os.path.exists(backup_file_path): + os.remove(backup_file_path) + shutil.move(LOCAL_FILE_PATH, backup_file_path) + + response = requests.get(REMOTE_FILE_URL, stream=True) + if response.status_code == 200: + with open(LOCAL_FILE_PATH, "wb") as local_zip: + for chunk in response.iter_content(chunk_size=8192): + local_zip.write(chunk) + else: + if os.path.exists(backup_file_path): + shutil.move(backup_file_path, LOCAL_FILE_PATH) + raise HTTPException(status_code=500, detail="Failed to download CWE model") + + if os.path.exists(CWE_MODEL_FOLDER_PATH): + shutil.rmtree(CWE_MODEL_FOLDER_PATH) + + with zipfile.ZipFile(LOCAL_FILE_PATH, 'r') as zip_ref: + zip_ref.extractall(APP_PATH) + + if os.path.exists(backup_file_path): + os.remove(backup_file_path) + + return {"status": "success", "message": "Updated CWE model successfully"} + + except Exception as e: + if os.path.exists(backup_file_path): + shutil.move(backup_file_path, LOCAL_FILE_PATH) + raise HTTPException(status_code=500, detail=str(e)) from e + +if __name__ == "__main__": + import uvicorn + uvicorn.run( + "main:app", + host="0.0.0.0", + port=8000, + ssl_keyfile="ssl/key.pem", + ssl_certfile="ssl/cert.pem", + ssl_version=ssl.PROTOCOL_TLS_SERVER + ) diff --git a/cwe_api/requirements.txt b/cwe_api/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..35338c4405d28572fcb6952f589cfa695a633200 --- /dev/null +++ b/cwe_api/requirements.txt @@ -0,0 +1,5 @@ +fastapi==0.115.0 +uvicorn==0.31.0 +transformers==4.41.2 +torch +scikit-learn==1.4.2 \ No newline at end of file diff --git a/cwe_api/ssl/cert.pem b/cwe_api/ssl/cert.pem new file mode 100644 index 0000000000000000000000000000000000000000..281ff7a63a077fb15d450a7f308d28964e12f446 --- /dev/null +++ b/cwe_api/ssl/cert.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIFCTCCAvGgAwIBAgIUOr8UkdWMjoJpxc7Yk+0J1EkDu1cwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDkyNDEyNTIwOVoXDTI1MDky +NDEyNTIwOVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEApPgcDP594A4US5tMi4RxI/gSX9fzTz+XCnWyJLEGXPf6 +SPZY/SrfRwdGIrs8kgiX6vFfIgMqfv/bSbF4BPamvm72GBC1mb+tlVtRL6Vi18zk +kmJw3l2biiNlkiuFeTV24y0pMd0oMdL5zGNFittMbvI41znO8G7oABq2Rx0P8Tyh +kGVtGzU7q7tOICS4jaVsY5qUPR7sPq583kPiZr1zJoEgd91LytiCSxgpuD4ECod3 +fpKH6bizKsyRvYmN4ltp2OlkD/SucvNGkx1uqRcIPYH/46PfWC45myZvyz/RHsrB +gzllvirHX8Nph+rXaY0UIli0PzCZOLZAEs6J2cHshYeYDHLsMewgd1SgRH9Qk69e +lBVX6AphZPwrpop1BcXXZ1ZNIgTE+Ig/hdNXv0PUKcZuPYMeGxsWN/CarPig4qG7 +5Y63OYKNCbzctSigQMQgVuCRqUIdJBuGepTiTIlNOTmwhUfOafNuBquOTUoVkSmB +GlwnzjbGKC/kHJCS/tDTHNKedQBH0+yZU0kiOPfpnaFCTkJ55HTOYeiKSx/7gG7F +wlje+kESx+fibpu8qHIzjAtL9J8alnfM2W1XnXMFLGJoMsieujiEK+7/quTOVpc+ +AppU4FJyBL+JnclIFlRNUz29HXKjCtPp/93hNbrqgeoUtqb7czFJ/W7Me5nMI9kC +AwEAAaNTMFEwHQYDVR0OBBYEFGKAk0c9+tdUca2Oy5VqtFtA/J/IMB8GA1UdIwQY +MBaAFGKAk0c9+tdUca2Oy5VqtFtA/J/IMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggIBAGBLTKACpMc8sg0EQfv/zspuOTSSyxcv6LvsdzZpK8rsdetQ +zHqkAWequ3aR2YdIAGdbk10pgNw+20OEBX59hxML4zxYCXzSc61kAe3LrlMWD7Iv +EHGXopkjtIV5mJPijPQNDSujR4e9azIBX1POIBqHEAuYwpJIGBzQ4CI+KOLCBcmT +8eOja6Ix9EqFcwRTGaJGV1ypiJYsjPT+T6mxPp3Qkkt65K+Cf/I0gggl0oK8ApFf +N6/pAoNk6yJWj8t+kzRin7S9xlHA/++CbwiN2oooYL06DtBZIFi70VdAhtkPMW0E +SJWVQW42mvhm4Blj/ucdSsl3rwTHaoP6ZxX2wCfXYpDDeYDMEycwAuOhPWmR7PMr +IESrZg/JM1QtFSTyCBsSW5kGLI90IhAAuqCvFBr6WQ6rnn6PYI+2BIUyyzXvgRRs +0QxbB6SFfnJYqD/1wuElzpuo0fxovItGEctza/R2ty4hg4P0MWmRjiEMbhVmem3G +817DkAcXGfuHXGkAme2yQilzrHVwflcXpX72cpuAyeSAE7oYrD3j96A0ZIo8DFLP +wD9ZuSE1arK28TFNIsdi9V3WpMwnylZ/siaNxvA8+phQ5uLR1lFjxj95/lHYBTZP +bwPCZMzcY/vBqU7UIIKqPp6pBn6sCG+dk6JQBtwDYJ+ZISbHPZ+woeOuNAkm +-----END CERTIFICATE----- diff --git a/cwe_api/ssl/key.pem b/cwe_api/ssl/key.pem new file mode 100644 index 0000000000000000000000000000000000000000..b7cf0768faf75dae3016a15d46935e7303787381 --- /dev/null +++ b/cwe_api/ssl/key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCk+BwM/n3gDhRL +m0yLhHEj+BJf1/NPP5cKdbIksQZc9/pI9lj9Kt9HB0YiuzySCJfq8V8iAyp+/9tJ +sXgE9qa+bvYYELWZv62VW1EvpWLXzOSSYnDeXZuKI2WSK4V5NXbjLSkx3Sgx0vnM +Y0WK20xu8jjXOc7wbugAGrZHHQ/xPKGQZW0bNTuru04gJLiNpWxjmpQ9Huw+rnze +Q+JmvXMmgSB33UvK2IJLGCm4PgQKh3d+kofpuLMqzJG9iY3iW2nY6WQP9K5y80aT +HW6pFwg9gf/jo99YLjmbJm/LP9EeysGDOWW+Ksdfw2mH6tdpjRQiWLQ/MJk4tkAS +zonZweyFh5gMcuwx7CB3VKBEf1CTr16UFVfoCmFk/CuminUFxddnVk0iBMT4iD+F +01e/Q9Qpxm49gx4bGxY38Jqs+KDiobvljrc5go0JvNy1KKBAxCBW4JGpQh0kG4Z6 +lOJMiU05ObCFR85p824Gq45NShWRKYEaXCfONsYoL+QckJL+0NMc0p51AEfT7JlT +SSI49+mdoUJOQnnkdM5h6IpLH/uAbsXCWN76QRLH5+Jum7yocjOMC0v0nxqWd8zZ +bVedcwUsYmgyyJ66OIQr7v+q5M5Wlz4CmlTgUnIEv4mdyUgWVE1TPb0dcqMK0+n/ +3eE1uuqB6hS2pvtzMUn9bsx7mcwj2QIDAQABAoICAD+TovPbDFXR9z5gHnLSWuwa +ybIIPqu2/RW54CrF0ob3xoxh7E+9Owlbo0r1T9WT3EBeP2STRchmAHP5IiD/kalo +iA0kO2iCd3pNFA/BtEMooppLt+Xqu2vUUmuvvCPHWAcbO3oNGlWBUmXTiFSISX+k +eAVyMO92PpDzunXTnHZDAJzuicrnjBSV/fjjTgDA1PHZ+/FGPpO1mUfrb5BayMr5 +DrNDJNeK80PPjxyDQflw1buA60JGvT3sUupx3a1zv2oqIDcJ/EUuvm8p86DU0Icr +15s6Z3PLdMX3XTQ+ZvBwAX36G9zbDbU5US1hcx0pkq3d87E6oII7spvM31F9rBH3 +LmB1QDcFd98kfPX2I8raWzWLi2Nh31X9dGZssUgBSB5hPsapOFycPRUj7PzmZT1X +98kjtHGmB5mfNColXZ8L4KwLEcgaD4qYLuIxXVnD2or/nY2z3skkRKZ7t0iQuNy0 +su+xCv5vr9zSV5h+29qP9KcCQlpUZQ4fwkUim8BTmOUkg6C2Fk4Af+rlED3C93/8 +EgMKU3WXF1PKAugM1xK1vTSG3AI5W1S5S1gwXi71wDmMd4N6+evjRn4enLzl0ejq +STguCJp0XXG1hpKLWfxCJiBHpUTFH2pZk0tMPbPMg5cag1cELKd4aKWv1EYLSyOp +GlH9luJJN47b9suFkylFAoIBAQDcxXxSavbC+WXIHKmE3KMvbkzeLoLPpt9qbKTx +9Av7cOHjjZ1vJhcxtL0u+vCCIAtO6ejVMOW/2MdOo8iNg3JngVEBl45+UkIUJHw3 +8l7bUL9NlS2yFvPA+duYJajXrxChgOTooHf98dg/WPknKJAkNPddpWnqbKk56hLI +6YZXJSlEzfRRLBY7YIRLD4g0qMVw0TQfhTnfhzBi9vMuQnxzNvziu4sXcLhNzGbM +VtfO/vnlLWuQM1vDbtXFJ+xKPCd3wyWzfrsOAqkvoM/PlYEUg0whk40k3PRMSZAE +BmfdYMsgZ7OVRRIVdR34YyGpYjVQ1WuSQ6+0C7fLXcwHFcyLAoIBAQC/SxoEqkIR +Ap0vq0hR4G7kSExq6nFUM+Y/Km/U77uU3KW6IgyL0a6+ghfxyeQUGJum7k9FpL51 +aHrpkN8xbzrrD5ha1OT8GrQeudcgi40Z4A3bfgE/4rQMM57+YZjHCmJkrfSE+qSb +D4AGIvQ/QxAVnScMpSbRJ7a4RBTRrWTRhjYm9Wo8cR0sbGVTsFMDuXifgo4JOdqs +A3ebgJyoTDZW13PWUl9OeJg0t5m3zDvlMARSXIbIZ5cr57HuUT8bCGvdLEIMtude +E/EjCw1gpbTnBOw7/EfaNqJitlHTuGlymibZsWP9qcAihrM/JOJ1RuniMd9eYECa +xTq/TJgddumrAoIBADFl3tNyK5SkySllgGjCFaFfRMPSFGLyK7tcY+vfd27jayfH +YBqmoznz/wCsFXSIRv95RyessTuj/bSS4SUI7GhMfxukFbYeIbrwDvtVt/PKtYNe +hcL7b78WKRR6GLVp7oa0mIOUpESmAITsG5Ovd9EiKNdDU9eoSJU00s8xsWV+YyWK +D8i53J+ckP1alFd0C/maVddXsANb9G+wYelP5xjAjQUOgajVB+ggfD/sjo06cB/f +cdnqI/YurpZWwIA7xXj+qUjoamJQJhsKGHWSNjeoyTgaE9LTvbX0H81Gwaa/KbQu +f/g+ZDV45yPUyotj9hoIJvGrQ309wfo3GpfbQNECggEABq/mafFVu+h+fDMdpRcW +aU6JtVcavRnPW7ftDuGOiiaoeHhGPgEHfctocOBg4770egJsrP+UVOBhI5cX5k+A +HMV/yvUUSCfa0hQnDBE0zonBkkG2Y3hWWPLZBQV+ZL7dL63RFXD5nh4NmKlSJn/+ +qPLEcbG4SKl9vrVKkQXht11cuUHtA83kejAT3rgUPtYV/GWA95uZJFg+ryXw1AVO +BP0lJIHeqQyYLYeZjQ5Lrbhe/k4TvB+67iL8WltmvFgwBRxN9snSDrzduh7hydYh +G85VP8eS73LnfFHpTGBu9z2iTAvidfSgLdTNoVB1xinDdU/PAiqVN5PbeOKBks4c +XwKCAQEAgvpeNzUdeOFMFGfvoxQStOjimQi6xt67fSWJgCPv58EVV/0GyIXl94yh +cS+zkcsNklBu45J1UoltcKoMsIUOuij4QoPSuTT+tcTwrjXyg/leIsXJV+sSyVv3 +663CDc2VYlGaoCFm0Hr3gh/t/DNvLDceK5h9pHtLddxYZcqYwELpqMYIlHbPrldZ +IzTQnskASCAS6/kviw083zHzoi02o28aSZ4pOm7HCVAMmej1cCNRExFJyYJT04TZ +yZsEvY7+2yexN/ovd3yRAOeA08H+Hthk/lzA3bajqQ5VxLwCfUUKBDPZ3D59OaUN +EJr8pMU5iz/6vnw25BEVdS86F3DnNg== +-----END PRIVATE KEY----- diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..43e75ddfed69b8bc829d76cd68dc23630bdcdfc3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,66 @@ +version: "3" +services: + mongodb: + image: mongo:4.2.15 + container_name: mongo-auditforge + volumes: + - ./backend/mongo-data:/data/db + restart: always + ports: + - 127.0.0.1:27017:27017 + environment: + - MONGO_DB:auditforge + networks: + - backend + + auditforge-backend: + build: ./backend + image: auditforge-backend + container_name: auditforge-backend + volumes: + - ./backend/report-templates:/app/report-templates + - ./backend/src/config:/app/src/config + depends_on: + - mongodb + restart: always + ports: + - 4242:4242 + links: + - mongodb + networks: + - backend + - cwe-api-network + + auditforge-frontend: + build: ./frontend + image: auditforge-frontend + container_name: auditforge-frontend + restart: always + depends_on: + - auditforge-backend + ports: + - 8443:8443 + networks: + - backend + + auditforge-cwe-api: + build: + context: ./cwe_api + dockerfile: Dockerfile + args: + CWE_MODEL_URL: ${CWE_MODEL_URL} + CVSS_MODEL_URL: ${CVSS_MODEL_URL} + image: auditforge-cwe-api + container_name: auditforge-cwe-api + restart: always + networks: + - cwe-api-network + +volumes: + mongo-data: + +networks: + backend: + driver: bridge + cwe-api-network: + driver: bridge diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000000000000000000000000000000000..11c640a4f2ea46d83aeef6f1858af401da460fcb --- /dev/null +++ b/docs/README.md @@ -0,0 +1,85 @@ +

    + AuditForge Logo +

    + +

    AuditForge

    + +

    + + License + + + Build Status + + + Website Status + +

    + +

    + AuditForge (PwnDoc fork) is a pentest reporting application making it simple and easy to write your findings and generate a customizable Docx report. + The main goal is to have more time to search vulnerabilities and less time to write documentation by mutualizing data like vulnerabilities between users. +

    + +

    + Website +

    + +## Table of Contents +- [Features](#features) +- [Documentation](#documentation) +- [Our Team](#our-team) + +## Features + +- 🌐 Multiple Language support + +![Audit creation](gifs/create_audit.gif) + +- 🤖 CWE and CVSS recommendations powered by AI + +![Recommendation of CWE and CVSS](gifs/ia.gif) + +- 📊 Dashboard generation + +![Dashboards](gifs/dashboard.gif) + +- 📁 Multiple Data support +- 🎨 Great Customization + - Manage reusable Audit and Vulnerability Data + - Create Custom Sections + - Add custom fields to Vulnerabilities + +- 🛠️ Vulnerabilities Management +- 👥 Multi-User reporting +- 📄 Docx and PDF Report Generation + +![Report generation](gifs/exportar.gif) + +- 📝 Docx Template customization + +![Report document](gifs/reporte.gif) + +## Documentation + +You can find the documentation [here](https://caverav.github.io/auditforge/). + +## Activity + +

    + +

    + +## Contributors + +

    + + + +

    + +

    + + Buy Me A Coffee + +

    diff --git a/docs/_images/action_buttons.png b/docs/_images/action_buttons.png new file mode 100644 index 0000000000000000000000000000000000000000..ddcc9ffabf5ae24a66d2ec5f719c1191ca48a27d Binary files /dev/null and b/docs/_images/action_buttons.png differ diff --git a/docs/_images/adding_reviewer.png b/docs/_images/adding_reviewer.png new file mode 100644 index 0000000000000000000000000000000000000000..bcfd3d9b7761b80f122609f763af7ad26917d86e Binary files /dev/null and b/docs/_images/adding_reviewer.png differ diff --git a/docs/_images/approved_audit.png b/docs/_images/approved_audit.png new file mode 100644 index 0000000000000000000000000000000000000000..80382ce67d2a8c2df76de0843b5e67462f0c700c Binary files /dev/null and b/docs/_images/approved_audit.png differ diff --git a/docs/_images/approving_report.png b/docs/_images/approving_report.png new file mode 100644 index 0000000000000000000000000000000000000000..3047c260f7aa324e0c1ae2431d8384f91c424901 Binary files /dev/null and b/docs/_images/approving_report.png differ diff --git a/docs/_images/audit_custom_section_add.png b/docs/_images/audit_custom_section_add.png new file mode 100644 index 0000000000000000000000000000000000000000..5d72029a303f2e0b6a1455fdf551a03c0c1f4ca0 Binary files /dev/null and b/docs/_images/audit_custom_section_add.png differ diff --git a/docs/_images/audit_list_states.png b/docs/_images/audit_list_states.png new file mode 100644 index 0000000000000000000000000000000000000000..4e56af583365f1be96ebec80d03af06b2c4192ce Binary files /dev/null and b/docs/_images/audit_list_states.png differ diff --git a/docs/_images/audit_multiuser_different.png b/docs/_images/audit_multiuser_different.png new file mode 100644 index 0000000000000000000000000000000000000000..819553c11f75b4d28e7fa57739757980ea86b772 Binary files /dev/null and b/docs/_images/audit_multiuser_different.png differ diff --git a/docs/_images/audit_multiuser_same.png b/docs/_images/audit_multiuser_same.png new file mode 100644 index 0000000000000000000000000000000000000000..ba43b82ca18897e81b868df9d95cf514d97ff3b8 Binary files /dev/null and b/docs/_images/audit_multiuser_same.png differ diff --git a/docs/_images/audit_state_tooltip.png b/docs/_images/audit_state_tooltip.png new file mode 100644 index 0000000000000000000000000000000000000000..63b53177f3ea49c9260435e27fcd2e96dd711be8 Binary files /dev/null and b/docs/_images/audit_state_tooltip.png differ diff --git a/docs/_images/audits_general.png b/docs/_images/audits_general.png new file mode 100644 index 0000000000000000000000000000000000000000..d715ee486945a12a6d8748bd9a82b20230ae09fe Binary files /dev/null and b/docs/_images/audits_general.png differ diff --git a/docs/_images/audits_network.png b/docs/_images/audits_network.png new file mode 100644 index 0000000000000000000000000000000000000000..812a902ad9f6140f74fc2bd7757ee0f8b556a4d8 Binary files /dev/null and b/docs/_images/audits_network.png differ diff --git a/docs/_images/collaborators.png b/docs/_images/collaborators.png new file mode 100644 index 0000000000000000000000000000000000000000..72859a46c7bde0ba4987d6298112fa35083a8260 Binary files /dev/null and b/docs/_images/collaborators.png differ diff --git a/docs/_images/docx_archive.png b/docs/_images/docx_archive.png new file mode 100644 index 0000000000000000000000000000000000000000..5afdda6562df167e68d0e0680afe8e9dcd493259 Binary files /dev/null and b/docs/_images/docx_archive.png differ diff --git a/docs/_images/edit_state_graph.png b/docs/_images/edit_state_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..498b078614ce99f0fbe7c3ac5471e7309b8b25f7 Binary files /dev/null and b/docs/_images/edit_state_graph.png differ diff --git a/docs/_images/finding_add.png b/docs/_images/finding_add.png new file mode 100644 index 0000000000000000000000000000000000000000..5fcca496ef45da05ff40f623f44bd8a4ba8d1af2 Binary files /dev/null and b/docs/_images/finding_add.png differ diff --git a/docs/_images/finding_completed.png b/docs/_images/finding_completed.png new file mode 100644 index 0000000000000000000000000000000000000000..15debb0126051e2f514c412f3b825af411b64a35 Binary files /dev/null and b/docs/_images/finding_completed.png differ diff --git a/docs/_images/finding_update_vuln_button.png b/docs/_images/finding_update_vuln_button.png new file mode 100644 index 0000000000000000000000000000000000000000..c513ed7289a28f875f58d5db1cf6c55c2bf172d1 Binary files /dev/null and b/docs/_images/finding_update_vuln_button.png differ diff --git a/docs/_images/mark_for_review.png b/docs/_images/mark_for_review.png new file mode 100644 index 0000000000000000000000000000000000000000..908e94f2ff206688622992607a3d61595fd46e35 Binary files /dev/null and b/docs/_images/mark_for_review.png differ diff --git a/docs/_images/merge_vulns.png b/docs/_images/merge_vulns.png new file mode 100644 index 0000000000000000000000000000000000000000..209dac92c57b7629315bcc9bbfa8f7b3bcad08c9 Binary files /dev/null and b/docs/_images/merge_vulns.png differ diff --git a/docs/_images/new_updates_vulns.png b/docs/_images/new_updates_vulns.png new file mode 100644 index 0000000000000000000000000000000000000000..a501f8b55c6c49c153e30ad1e114e477365efe35 Binary files /dev/null and b/docs/_images/new_updates_vulns.png differ diff --git a/docs/_images/new_vuln.png b/docs/_images/new_vuln.png new file mode 100644 index 0000000000000000000000000000000000000000..131479b07082d2b1106f448ef895c8b6a9091bba Binary files /dev/null and b/docs/_images/new_vuln.png differ diff --git a/docs/_images/updates_vuln.png b/docs/_images/updates_vuln.png new file mode 100644 index 0000000000000000000000000000000000000000..50071708f86fe2cdd4d5d7854d25723e10bc658b Binary files /dev/null and b/docs/_images/updates_vuln.png differ diff --git a/docs/_sidebar.md b/docs/_sidebar.md new file mode 100644 index 0000000000000000000000000000000000000000..06d1cc1c141a18a56a1a40303a5be245ea0566d6 --- /dev/null +++ b/docs/_sidebar.md @@ -0,0 +1,8 @@ +- [Installation](installation.md) +- [Data](data.md) +- [Roles](roles.md) +- [Vulnerabilities](vulnerabilities.md) +- [Audits](audits.md) +- [Docx Template](docxtemplate.md) +- [Debug](debug.md) +- [API Documentation](/auditforge/api/ ":ignore API Swagger File") diff --git a/docs/api/favicon-16x16.png b/docs/api/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..8b194e617af1c135e6b37939591d24ac3a5efa18 Binary files /dev/null and b/docs/api/favicon-16x16.png differ diff --git a/docs/api/favicon-32x32.png b/docs/api/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..249737fe44558e679f0b67134e274461d988fa98 Binary files /dev/null and b/docs/api/favicon-32x32.png differ diff --git a/docs/api/index.css b/docs/api/index.css new file mode 100644 index 0000000000000000000000000000000000000000..f2376fdaa842b12ecf6a191409ff0b0a03c4b0f8 --- /dev/null +++ b/docs/api/index.css @@ -0,0 +1,16 @@ +html { + box-sizing: border-box; + overflow: -moz-scrollbars-vertical; + overflow-y: scroll; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +body { + margin: 0; + background: #fafafa; +} diff --git a/docs/api/index.html b/docs/api/index.html new file mode 100644 index 0000000000000000000000000000000000000000..84ae62d3dad6004f2ba250cf69b4148eeb7c253d --- /dev/null +++ b/docs/api/index.html @@ -0,0 +1,19 @@ + + + + + + Swagger UI + + + + + + + +
    + + + + + diff --git a/docs/api/oauth2-redirect.html b/docs/api/oauth2-redirect.html new file mode 100644 index 0000000000000000000000000000000000000000..56409171811d4594c24ef540844741b8d12e9da2 --- /dev/null +++ b/docs/api/oauth2-redirect.html @@ -0,0 +1,79 @@ + + + + Swagger UI: OAuth2 Redirect + + + + + diff --git a/docs/api/swagger-initializer.js b/docs/api/swagger-initializer.js new file mode 100644 index 0000000000000000000000000000000000000000..05500b37e1a1deff1b2042ee84dd27a8bd713af8 --- /dev/null +++ b/docs/api/swagger-initializer.js @@ -0,0 +1,20 @@ +window.onload = function() { + // + + // the following lines will be replaced by docker/configurator, when it runs in a docker-container + window.ui = SwaggerUIBundle({ + url: "swagger.json", + dom_id: '#swagger-ui', + deepLinking: true, + presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIStandalonePreset + ], + plugins: [ + SwaggerUIBundle.plugins.DownloadUrl + ], + layout: "StandaloneLayout" + }); + + // +}; diff --git a/docs/api/swagger-ui-bundle.js b/docs/api/swagger-ui-bundle.js new file mode 100644 index 0000000000000000000000000000000000000000..94a4d2c948f37c7163a1ec75237bea6213fdc7f0 --- /dev/null +++ b/docs/api/swagger-ui-bundle.js @@ -0,0 +1,3 @@ +/*! For license information please see swagger-ui-bundle.js.LICENSE.txt */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUIBundle=t():e.SwaggerUIBundle=t()}(this,(()=>(()=>{var e={17967:(e,t)=>{"use strict";t.N=void 0;var n=/^([^\w]*)(javascript|data|vbscript)/im,r=/&#(\w+)(^\w|;)?/g,o=/&(newline|tab);/gi,a=/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim,i=/^.+(:|:)/gim,s=[".","/"];t.N=function(e){var t,l=(t=e||"",t.replace(r,(function(e,t){return String.fromCharCode(t)}))).replace(o,"").replace(a,"").trim();if(!l)return"about:blank";if(function(e){return s.indexOf(e[0])>-1}(l))return l;var u=l.match(i);if(!u)return l;var c=u[0];return n.test(c)?"about:blank":l}},53795:(e,t,n)=>{"use strict";n.d(t,{Z:()=>j});var r=n(23101),o=n.n(r),a=n(61125),i=n.n(a),s=n(11882),l=n.n(s),u=n(97606),c=n.n(u),p=n(67294),f=n(43393);function h(e){return h="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h(e)}function d(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=function(e,t){return function(n){if("string"==typeof n)return(0,f.is)(t[n],e[n]);if(Array.isArray(n))return(0,f.is)(x(t,n),x(e,n));throw new TypeError("Invalid key: expected Array or string: "+n)}}(t,n),o=e||Object.keys(function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{};return!_(this.updateOnProps,this.props,e,"updateOnProps")||!_(this.updateOnStates,this.state,t,"updateOnStates")}}],r&&d(n.prototype,r),o&&d(n,o),t}(p.Component);var A=n(23930),C=n.n(A),k=n(45697),O=n.n(k);class j extends S{constructor(){super(...arguments),i()(this,"getModelName",(e=>-1!==l()(e).call(e,"#/definitions/")?e.replace(/^.*#\/definitions\//,""):-1!==l()(e).call(e,"#/components/schemas/")?e.replace(/^.*#\/components\/schemas\//,""):void 0)),i()(this,"getRefSchema",(e=>{let{specSelectors:t}=this.props;return t.findDefinition(e)}))}render(){let{getComponent:e,getConfigs:t,specSelectors:r,schema:a,required:i,name:s,isRef:l,specPath:u,displayName:c,includeReadOnly:f,includeWriteOnly:h}=this.props;const d=e("ObjectModel"),m=e("ArrayModel"),g=e("PrimitiveModel");let y="object",v=a&&a.get("$$ref");if(!s&&v&&(s=this.getModelName(v)),!a&&v&&(a=this.getRefSchema(s)),!a)return p.createElement("span",{className:"model model-title"},p.createElement("span",{className:"model-title__text"},c||s),p.createElement("img",{src:n(2517),height:"20px",width:"20px"}));const b=r.isOAS3()&&a.get("deprecated");switch(l=void 0!==l?l:!!v,y=a&&a.get("type")||y,y){case"object":return p.createElement(d,o()({className:"object"},this.props,{specPath:u,getConfigs:t,schema:a,name:s,deprecated:b,isRef:l,includeReadOnly:f,includeWriteOnly:h}));case"array":return p.createElement(m,o()({className:"array"},this.props,{getConfigs:t,schema:a,name:s,deprecated:b,required:i,includeReadOnly:f,includeWriteOnly:h}));default:return p.createElement(g,o()({},this.props,{getComponent:e,getConfigs:t,schema:a,name:s,deprecated:b,required:i}))}}}i()(j,"propTypes",{schema:c()(C()).isRequired,getComponent:O().func.isRequired,getConfigs:O().func.isRequired,specSelectors:O().object.isRequired,name:O().string,displayName:O().string,isRef:O().bool,required:O().bool,expandDepth:O().number,depth:O().number,specPath:C().list.isRequired,includeReadOnly:O().bool,includeWriteOnly:O().bool})},5623:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var r=n(61125),o=n.n(r),a=n(28222),i=n.n(a),s=n(67294),l=n(84564),u=n.n(l),c=n(90242),p=n(27504);class f extends s.Component{constructor(e,t){super(e,t),o()(this,"getDefinitionUrl",(()=>{let{specSelectors:e}=this.props;return new(u())(e.url(),p.Z.location).toString()}));let{getConfigs:n}=e,{validatorUrl:r}=n();this.state={url:this.getDefinitionUrl(),validatorUrl:void 0===r?"https://validator.swagger.io/validator":r}}UNSAFE_componentWillReceiveProps(e){let{getConfigs:t}=e,{validatorUrl:n}=t();this.setState({url:this.getDefinitionUrl(),validatorUrl:void 0===n?"https://validator.swagger.io/validator":n})}render(){let{getConfigs:e}=this.props,{spec:t}=e(),n=(0,c.Nm)(this.state.validatorUrl);return"object"==typeof t&&i()(t).length?null:this.state.url&&(0,c.hW)(this.state.validatorUrl)&&(0,c.hW)(this.state.url)?s.createElement("span",{className:"float-right"},s.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:`${n}/debug?url=${encodeURIComponent(this.state.url)}`},s.createElement(h,{src:`${n}?url=${encodeURIComponent(this.state.url)}`,alt:"Online validator badge"}))):null}}class h extends s.Component{constructor(e){super(e),this.state={loaded:!1,error:!1}}componentDidMount(){const e=new Image;e.onload=()=>{this.setState({loaded:!0})},e.onerror=()=>{this.setState({error:!0})},e.src=this.props.src}UNSAFE_componentWillReceiveProps(e){if(e.src!==this.props.src){const t=new Image;t.onload=()=>{this.setState({loaded:!0})},t.onerror=()=>{this.setState({error:!0})},t.src=e.src}}render(){return this.state.error?s.createElement("img",{alt:"Error"}):this.state.loaded?s.createElement("img",{src:this.props.src,alt:this.props.alt}):null}}},94994:(e,t,n)=>{"use strict";n.d(t,{Z:()=>ye,s:()=>ve});var r=n(67294),o=n(89927);function a(e,t){if(Array.prototype.indexOf)return e.indexOf(t);for(var n=0,r=e.length;n=0;n--)!0===t(e[n])&&e.splice(n,1)}function s(e){throw new Error("Unhandled case for value: '".concat(e,"'"))}var l=function(){function e(e){void 0===e&&(e={}),this.tagName="",this.attrs={},this.innerHTML="",this.whitespaceRegex=/\s+/,this.tagName=e.tagName||"",this.attrs=e.attrs||{},this.innerHTML=e.innerHtml||e.innerHTML||""}return e.prototype.setTagName=function(e){return this.tagName=e,this},e.prototype.getTagName=function(){return this.tagName||""},e.prototype.setAttr=function(e,t){return this.getAttrs()[e]=t,this},e.prototype.getAttr=function(e){return this.getAttrs()[e]},e.prototype.setAttrs=function(e){return Object.assign(this.getAttrs(),e),this},e.prototype.getAttrs=function(){return this.attrs||(this.attrs={})},e.prototype.setClass=function(e){return this.setAttr("class",e)},e.prototype.addClass=function(e){for(var t,n=this.getClass(),r=this.whitespaceRegex,o=n?n.split(r):[],i=e.split(r);t=i.shift();)-1===a(o,t)&&o.push(t);return this.getAttrs().class=o.join(" "),this},e.prototype.removeClass=function(e){for(var t,n=this.getClass(),r=this.whitespaceRegex,o=n?n.split(r):[],i=e.split(r);o.length&&(t=i.shift());){var s=a(o,t);-1!==s&&o.splice(s,1)}return this.getAttrs().class=o.join(" "),this},e.prototype.getClass=function(){return this.getAttrs().class||""},e.prototype.hasClass=function(e){return-1!==(" "+this.getClass()+" ").indexOf(" "+e+" ")},e.prototype.setInnerHTML=function(e){return this.innerHTML=e,this},e.prototype.setInnerHtml=function(e){return this.setInnerHTML(e)},e.prototype.getInnerHTML=function(){return this.innerHTML||""},e.prototype.getInnerHtml=function(){return this.getInnerHTML()},e.prototype.toAnchorString=function(){var e=this.getTagName(),t=this.buildAttrsStr();return["<",e,t=t?" "+t:"",">",this.getInnerHtml(),""].join("")},e.prototype.buildAttrsStr=function(){if(!this.attrs)return"";var e=this.getAttrs(),t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n+'="'+e[n]+'"');return t.join(" ")},e}();var u=function(){function e(e){void 0===e&&(e={}),this.newWindow=!1,this.truncate={},this.className="",this.newWindow=e.newWindow||!1,this.truncate=e.truncate||{},this.className=e.className||""}return e.prototype.build=function(e){return new l({tagName:"a",attrs:this.createAttrs(e),innerHtml:this.processAnchorText(e.getAnchorText())})},e.prototype.createAttrs=function(e){var t={href:e.getAnchorHref()},n=this.createCssClass(e);return n&&(t.class=n),this.newWindow&&(t.target="_blank",t.rel="noopener noreferrer"),this.truncate&&this.truncate.length&&this.truncate.length=s)return l.host.length==t?(l.host.substr(0,t-o)+n).substr(0,s+r):i(c,s).substr(0,s+r);var p="";if(l.path&&(p+="/"+l.path),l.query&&(p+="?"+l.query),p){if((c+p).length>=s)return(c+p).length==t?(c+p).substr(0,t):(c+i(p,s-c.length)).substr(0,s+r);c+=p}if(l.fragment){var f="#"+l.fragment;if((c+f).length>=s)return(c+f).length==t?(c+f).substr(0,t):(c+i(f,s-c.length)).substr(0,s+r);c+=f}if(l.scheme&&l.host){var h=l.scheme+"://";if((c+h).length0&&(d=c.substr(-1*Math.floor(s/2))),(c.substr(0,Math.ceil(s/2))+n+d).substr(0,s+r)}(e,n):"middle"===r?function(e,t,n){if(e.length<=t)return e;var r,o;null==n?(n="…",r=8,o=3):(r=n.length,o=n.length);var a=t-o,i="";return a>0&&(i=e.substr(-1*Math.floor(a/2))),(e.substr(0,Math.ceil(a/2))+n+i).substr(0,a+r)}(e,n):function(e,t,n){return function(e,t,n){var r;return e.length>t&&(null==n?(n="…",r=3):r=n.length,e=e.substring(0,t-r)+n),e}(e,t,n)}(e,n)},e}(),c=function(){function e(e){this.__jsduckDummyDocProp=null,this.matchedText="",this.offset=0,this.tagBuilder=e.tagBuilder,this.matchedText=e.matchedText,this.offset=e.offset}return e.prototype.getMatchedText=function(){return this.matchedText},e.prototype.setOffset=function(e){this.offset=e},e.prototype.getOffset=function(){return this.offset},e.prototype.getCssClassSuffixes=function(){return[this.getType()]},e.prototype.buildTag=function(){return this.tagBuilder.build(this)},e}(),p=function(e,t){return p=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},p(e,t)};function f(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}p(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var h=function(){return h=Object.assign||function(e){for(var t,n=1,r=arguments.length;n-1},e.isValidUriScheme=function(e){var t=e.match(this.uriSchemeRegex),n=t&&t[0].toLowerCase();return"javascript:"!==n&&"vbscript:"!==n},e.urlMatchDoesNotHaveProtocolOrDot=function(e,t){return!(!e||t&&this.hasFullProtocolRegex.test(t)||-1!==e.indexOf("."))},e.urlMatchDoesNotHaveAtLeastOneWordChar=function(e,t){return!(!e||!t)&&(!this.hasFullProtocolRegex.test(t)&&!this.hasWordCharAfterProtocolRegex.test(e))},e.hasFullProtocolRegex=/^[A-Za-z][-.+A-Za-z0-9]*:\/\//,e.uriSchemeRegex=/^[A-Za-z][-.+A-Za-z0-9]*:/,e.hasWordCharAfterProtocolRegex=new RegExp(":[^\\s]*?["+k+"]"),e.ipRegex=/[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?(:[0-9]*)?\/?$/,e}(),V=(d=new RegExp("[/?#](?:["+I+"\\-+&@#/%=~_()|'$*\\[\\]{}?!:,.;^✓]*["+I+"\\-+&@#/%=~_()|'$*\\[\\]{}✓])?"),new RegExp(["(?:","(",/(?:[A-Za-z][-.+A-Za-z0-9]{0,63}:(?![A-Za-z][-.+A-Za-z0-9]{0,63}:\/\/)(?!\d+\/?)(?:\/\/)?)/.source,D(2),")","|","(","(//)?",/(?:www\.)/.source,D(6),")","|","(","(//)?",D(10)+"\\.",B.source,"(?![-"+T+"])",")",")","(?::[0-9]+)?","(?:"+d.source+")?"].join(""),"gi")),H=new RegExp("["+I+"]"),W=function(e){function t(t){var n=e.call(this,t)||this;return n.stripPrefix={scheme:!0,www:!0},n.stripTrailingSlash=!0,n.decodePercentEncoding=!0,n.matcherRegex=V,n.wordCharRegExp=H,n.stripPrefix=t.stripPrefix,n.stripTrailingSlash=t.stripTrailingSlash,n.decodePercentEncoding=t.decodePercentEncoding,n}return f(t,e),t.prototype.parseMatches=function(e){for(var t,n=this.matcherRegex,r=this.stripPrefix,o=this.stripTrailingSlash,a=this.decodePercentEncoding,i=this.tagBuilder,s=[],l=function(){var n=t[0],l=t[1],c=t[4],p=t[5],f=t[9],h=t.index,d=p||f,m=e.charAt(h-1);if(!$.isValid(n,l))return"continue";if(h>0&&"@"===m)return"continue";if(h>0&&d&&u.wordCharRegExp.test(m))return"continue";if(/\?$/.test(n)&&(n=n.substr(0,n.length-1)),u.matchHasUnbalancedClosingParen(n))n=n.substr(0,n.length-1);else{var g=u.matchHasInvalidCharAfterTld(n,l);g>-1&&(n=n.substr(0,g))}var y=["http://","https://"].find((function(e){return!!l&&-1!==l.indexOf(e)}));if(y){var v=n.indexOf(y);n=n.substr(v),l=l.substr(v),h+=v}var w=l?"scheme":c?"www":"tld",E=!!l;s.push(new b({tagBuilder:i,matchedText:n,offset:h,urlMatchType:w,url:n,protocolUrlMatch:E,protocolRelativeMatch:!!d,stripPrefix:r,stripTrailingSlash:o,decodePercentEncoding:a}))},u=this;null!==(t=n.exec(e));)l();return s},t.prototype.matchHasUnbalancedClosingParen=function(e){var t,n=e.charAt(e.length-1);if(")"===n)t="(";else if("]"===n)t="[";else{if("}"!==n)return!1;t="{"}for(var r=0,o=0,a=e.length-1;o-1&&a-i<=140){var o=e.slice(i,a),s=new g({tagBuilder:t,matchedText:o,offset:i,serviceName:n,hashtag:o.slice(1)});r.push(s)}}},t}(w),G=["twitter","facebook","instagram","tiktok"],Z=new RegExp("".concat(/(?:(?:(?:(\+)?\d{1,3}[-\040.]?)?\(?\d{3}\)?[-\040.]?\d{3}[-\040.]?\d{4})|(?:(\+)(?:9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)[-\040.]?(?:\d[-\040.]?){6,12}\d+))([,;]+[0-9]+#?)*/.source,"|").concat(/(0([1-9]{1}-?[1-9]\d{3}|[1-9]{2}-?\d{3}|[1-9]{2}\d{1}-?\d{2}|[1-9]{2}\d{2}-?\d{1})-?\d{4}|0[789]0-?\d{4}-?\d{4}|050-?\d{4}-?\d{4})/.source),"g"),Y=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.matcherRegex=Z,t}return f(t,e),t.prototype.parseMatches=function(e){for(var t,n=this.matcherRegex,r=this.tagBuilder,o=[];null!==(t=n.exec(e));){var a=t[0],i=a.replace(/[^0-9,;#]/g,""),s=!(!t[1]&&!t[2]),l=0==t.index?"":e.substr(t.index-1,1),u=e.substr(t.index+a.length,1),c=!l.match(/\d/)&&!u.match(/\d/);this.testMatch(t[3])&&this.testMatch(a)&&c&&o.push(new v({tagBuilder:r,matchedText:a,offset:t.index,number:i,plusSign:s}))}return o},t.prototype.testMatch=function(e){return _.test(e)},t}(w),Q=new RegExp("@[_".concat(I,"]{1,50}(?![_").concat(I,"])"),"g"),X=new RegExp("@[_.".concat(I,"]{1,30}(?![_").concat(I,"])"),"g"),ee=new RegExp("@[-_.".concat(I,"]{1,50}(?![-_").concat(I,"])"),"g"),te=new RegExp("@[_.".concat(I,"]{1,23}[_").concat(I,"](?![_").concat(I,"])"),"g"),ne=new RegExp("[^"+I+"]"),re=function(e){function t(t){var n=e.call(this,t)||this;return n.serviceName="twitter",n.matcherRegexes={twitter:Q,instagram:X,soundcloud:ee,tiktok:te},n.nonWordCharRegex=ne,n.serviceName=t.serviceName,n}return f(t,e),t.prototype.parseMatches=function(e){var t,n=this.serviceName,r=this.matcherRegexes[this.serviceName],o=this.nonWordCharRegex,a=this.tagBuilder,i=[];if(!r)return i;for(;null!==(t=r.exec(e));){var s=t.index,l=e.charAt(s-1);if(0===s||o.test(l)){var u=t[0].replace(/\.+$/g,""),c=u.slice(1);i.push(new y({tagBuilder:a,matchedText:u,offset:s,serviceName:n,mention:c}))}}return i},t}(w);function oe(e,t){for(var n,r=t.onOpenTag,o=t.onCloseTag,a=t.onText,i=t.onComment,l=t.onDoctype,u=new ae,c=0,p=e.length,f=0,d=0,m=u;c"===e?(m=new ae(h(h({},m),{name:W()})),H()):E.test(e)||x.test(e)||":"===e||$()}function w(e){">"===e?$():E.test(e)?f=3:$()}function _(e){S.test(e)||("/"===e?f=12:">"===e?H():"<"===e?V():"="===e||A.test(e)||C.test(e)?$():f=5)}function k(e){S.test(e)?f=6:"/"===e?f=12:"="===e?f=7:">"===e?H():"<"===e?V():A.test(e)&&$()}function O(e){S.test(e)||("/"===e?f=12:"="===e?f=7:">"===e?H():"<"===e?V():A.test(e)?$():f=5)}function j(e){S.test(e)||('"'===e?f=8:"'"===e?f=9:/[>=`]/.test(e)?$():"<"===e?V():f=10)}function T(e){'"'===e&&(f=11)}function I(e){"'"===e&&(f=11)}function N(e){S.test(e)?f=4:">"===e?H():"<"===e&&V()}function P(e){S.test(e)?f=4:"/"===e?f=12:">"===e?H():"<"===e?V():(f=4,c--)}function R(e){">"===e?(m=new ae(h(h({},m),{isClosing:!0})),H()):f=4}function M(t){"--"===e.substr(c,2)?(c+=2,m=new ae(h(h({},m),{type:"comment"})),f=14):"DOCTYPE"===e.substr(c,7).toUpperCase()?(c+=7,m=new ae(h(h({},m),{type:"doctype"})),f=20):$()}function D(e){"-"===e?f=15:">"===e?$():f=16}function L(e){"-"===e?f=18:">"===e?$():f=16}function B(e){"-"===e&&(f=17)}function F(e){f="-"===e?18:16}function U(e){">"===e?H():"!"===e?f=19:"-"===e||(f=16)}function z(e){"-"===e?f=17:">"===e?H():f=16}function q(e){">"===e?H():"<"===e&&V()}function $(){f=0,m=u}function V(){f=1,m=new ae({idx:c})}function H(){var t=e.slice(d,m.idx);t&&a(t,d),"comment"===m.type?i(m.idx):"doctype"===m.type?l(m.idx):(m.isOpening&&r(m.name,m.idx),m.isClosing&&o(m.name,m.idx)),$(),d=c+1}function W(){var t=m.idx+(m.isClosing?2:1);return e.slice(t,c).toLowerCase()}d=0&&r++},onText:function(e,n){if(0===r){var a=function(e,t){if(!t.global)throw new Error("`splitRegex` must have the 'g' flag set");for(var n,r=[],o=0;n=t.exec(e);)r.push(e.substring(o,n.index)),r.push(n[0]),o=n.index+n[0].length;return r.push(e.substring(o)),r}(e,/( | |<|<|>|>|"|"|')/gi),i=n;a.forEach((function(e,n){if(n%2==0){var r=t.parseText(e,i);o.push.apply(o,r)}i+=e.length}))}},onCloseTag:function(e){n.indexOf(e)>=0&&(r=Math.max(r-1,0))},onComment:function(e){},onDoctype:function(e){}}),o=this.compactMatches(o),o=this.removeUnwantedMatches(o)},e.prototype.compactMatches=function(e){e.sort((function(e,t){return e.getOffset()-t.getOffset()}));for(var t=0;to?t:t+1;e.splice(i,1);continue}if(e[t+1].getOffset()/g,">"));for(var t=this.parse(e),n=[],r=0,o=0,a=t.length;o/i.test(e)}function ue(){var e=[],t=new ie({stripPrefix:!1,url:!0,email:!0,replaceFn:function(t){switch(t.getType()){case"url":e.push({text:t.matchedText,url:t.getUrl()});break;case"email":e.push({text:t.matchedText,url:"mailto:"+t.getEmail().replace(/^mailto:/i,"")})}return!1}});return{links:e,autolinker:t}}function ce(e){var t,n,r,o,a,i,s,l,u,c,p,f,h,d,m=e.tokens,g=null;for(n=0,r=m.length;n=0;t--)if("link_close"!==(a=o[t]).type){if("htmltag"===a.type&&(d=a.content,/^\s]/i.test(d)&&p>0&&p--,le(a.content)&&p++),!(p>0)&&"text"===a.type&&se.test(a.content)){if(g||(f=(g=ue()).links,h=g.autolinker),i=a.content,f.length=0,h.link(i),!f.length)continue;for(s=[],c=a.level,l=0;l({useUnsafeMarkdown:!1})};const ye=ge;function ve(e){let{useUnsafeMarkdown:t=!1}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n=t,r=t?[]:["style","class"];return t&&!ve.hasWarnedAboutDeprecation&&(console.warn("useUnsafeMarkdown display configuration parameter is deprecated since >3.26.0 and will be removed in v4.0.0."),ve.hasWarnedAboutDeprecation=!0),he().sanitize(e,{ADD_ATTR:["target"],FORBID_TAGS:["style","form"],ALLOW_DATA_ATTR:n,FORBID_ATTR:r})}ve.hasWarnedAboutDeprecation=!1},45308:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>f});var r,o=n(86),a=n.n(o),i=n(8712),s=n.n(i),l=n(90242),u=n(27621);const c=n(95102),p={},f=p;a()(r=s()(c).call(c)).call(r,(function(e){if("./index.js"===e)return;let t=c(e);p[(0,l.Zl)(e)]=t.default?t.default:t})),p.SafeRender=u.default},55812:(e,t,n)=>{"use strict";n.r(t),n.d(t,{AUTHORIZE:()=>f,AUTHORIZE_OAUTH2:()=>m,CONFIGURE_AUTH:()=>y,LOGOUT:()=>h,PRE_AUTHORIZE_OAUTH2:()=>d,RESTORE_AUTHORIZATION:()=>v,SHOW_AUTH_POPUP:()=>p,VALIDATE:()=>g,authPopup:()=>M,authorize:()=>w,authorizeAccessCodeWithBasicAuthentication:()=>T,authorizeAccessCodeWithFormParams:()=>j,authorizeApplication:()=>O,authorizeOauth2:()=>A,authorizeOauth2WithPersistOption:()=>C,authorizePassword:()=>k,authorizeRequest:()=>I,authorizeWithPersistOption:()=>E,configureAuth:()=>N,logout:()=>x,logoutWithPersistOption:()=>_,persistAuthorizationIfNeeded:()=>R,preAuthorizeImplicit:()=>S,restoreAuthorization:()=>P,showDefinitions:()=>b});var r=n(35627),o=n.n(r),a=n(76986),i=n.n(a),s=n(84564),l=n.n(s),u=n(27504),c=n(90242);const p="show_popup",f="authorize",h="logout",d="pre_authorize_oauth2",m="authorize_oauth2",g="validate",y="configure_auth",v="restore_authorization";function b(e){return{type:p,payload:e}}function w(e){return{type:f,payload:e}}const E=e=>t=>{let{authActions:n}=t;n.authorize(e),n.persistAuthorizationIfNeeded()};function x(e){return{type:h,payload:e}}const _=e=>t=>{let{authActions:n}=t;n.logout(e),n.persistAuthorizationIfNeeded()},S=e=>t=>{let{authActions:n,errActions:r}=t,{auth:a,token:i,isValid:s}=e,{schema:l,name:c}=a,p=l.get("flow");delete u.Z.swaggerUIRedirectOauth2,"accessCode"===p||s||r.newAuthErr({authId:c,source:"auth",level:"warning",message:"Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"}),i.error?r.newAuthErr({authId:c,source:"auth",level:"error",message:o()(i)}):n.authorizeOauth2WithPersistOption({auth:a,token:i})};function A(e){return{type:m,payload:e}}const C=e=>t=>{let{authActions:n}=t;n.authorizeOauth2(e),n.persistAuthorizationIfNeeded()},k=e=>t=>{let{authActions:n}=t,{schema:r,name:o,username:a,password:s,passwordType:l,clientId:u,clientSecret:p}=e,f={grant_type:"password",scope:e.scopes.join(" "),username:a,password:s},h={};switch(l){case"request-body":!function(e,t,n){t&&i()(e,{client_id:t});n&&i()(e,{client_secret:n})}(f,u,p);break;case"basic":h.Authorization="Basic "+(0,c.r3)(u+":"+p);break;default:console.warn(`Warning: invalid passwordType ${l} was passed, not including client id and secret`)}return n.authorizeRequest({body:(0,c.GZ)(f),url:r.get("tokenUrl"),name:o,headers:h,query:{},auth:e})};const O=e=>t=>{let{authActions:n}=t,{schema:r,scopes:o,name:a,clientId:i,clientSecret:s}=e,l={Authorization:"Basic "+(0,c.r3)(i+":"+s)},u={grant_type:"client_credentials",scope:o.join(" ")};return n.authorizeRequest({body:(0,c.GZ)(u),name:a,url:r.get("tokenUrl"),auth:e,headers:l})},j=e=>{let{auth:t,redirectUrl:n}=e;return e=>{let{authActions:r}=e,{schema:o,name:a,clientId:i,clientSecret:s,codeVerifier:l}=t,u={grant_type:"authorization_code",code:t.code,client_id:i,client_secret:s,redirect_uri:n,code_verifier:l};return r.authorizeRequest({body:(0,c.GZ)(u),name:a,url:o.get("tokenUrl"),auth:t})}},T=e=>{let{auth:t,redirectUrl:n}=e;return e=>{let{authActions:r}=e,{schema:o,name:a,clientId:i,clientSecret:s,codeVerifier:l}=t,u={Authorization:"Basic "+(0,c.r3)(i+":"+s)},p={grant_type:"authorization_code",code:t.code,client_id:i,redirect_uri:n,code_verifier:l};return r.authorizeRequest({body:(0,c.GZ)(p),name:a,url:o.get("tokenUrl"),auth:t,headers:u})}},I=e=>t=>{let n,{fn:r,getConfigs:a,authActions:s,errActions:u,oas3Selectors:c,specSelectors:p,authSelectors:f}=t,{body:h,query:d={},headers:m={},name:g,url:y,auth:v}=e,{additionalQueryStringParams:b}=f.getConfigs()||{};if(p.isOAS3()){let e=c.serverEffectiveValue(c.selectedServer());n=l()(y,e,!0)}else n=l()(y,p.url(),!0);"object"==typeof b&&(n.query=i()({},n.query,b));const w=n.toString();let E=i()({Accept:"application/json, text/plain, */*","Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"},m);r.fetch({url:w,method:"post",headers:E,query:d,body:h,requestInterceptor:a().requestInterceptor,responseInterceptor:a().responseInterceptor}).then((function(e){let t=JSON.parse(e.data),n=t&&(t.error||""),r=t&&(t.parseError||"");e.ok?n||r?u.newAuthErr({authId:g,level:"error",source:"auth",message:o()(t)}):s.authorizeOauth2WithPersistOption({auth:v,token:t}):u.newAuthErr({authId:g,level:"error",source:"auth",message:e.statusText})})).catch((e=>{let t=new Error(e).message;if(e.response&&e.response.data){const n=e.response.data;try{const e="string"==typeof n?JSON.parse(n):n;e.error&&(t+=`, error: ${e.error}`),e.error_description&&(t+=`, description: ${e.error_description}`)}catch(e){}}u.newAuthErr({authId:g,level:"error",source:"auth",message:t})}))};function N(e){return{type:y,payload:e}}function P(e){return{type:v,payload:e}}const R=()=>e=>{let{authSelectors:t,getConfigs:n}=e;if(n().persistAuthorization){const e=t.authorized();localStorage.setItem("authorized",o()(e.toJS()))}},M=(e,t)=>()=>{u.Z.swaggerUIRedirectOauth2=t,u.Z.open(e)}},93705:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u,preauthorizeApiKey:()=>p,preauthorizeBasic:()=>c});var r=n(11189),o=n.n(r),a=n(43962),i=n(55812),s=n(60035),l=n(48302);function u(){return{afterLoad(e){this.rootInjects=this.rootInjects||{},this.rootInjects.initOAuth=e.authActions.configureAuth,this.rootInjects.preauthorizeApiKey=o()(p).call(p,null,e),this.rootInjects.preauthorizeBasic=o()(c).call(c,null,e)},statePlugins:{auth:{reducers:a.default,actions:i,selectors:s},spec:{wrapActions:l}}}}function c(e,t,n,r){const{authActions:{authorize:o},specSelectors:{specJson:a,isOAS3:i}}=e,s=i()?["components","securitySchemes"]:["securityDefinitions"],l=a().getIn([...s,t]);return l?o({[t]:{value:{username:n,password:r},schema:l.toJS()}}):null}function p(e,t,n){const{authActions:{authorize:r},specSelectors:{specJson:o,isOAS3:a}}=e,i=a()?["components","securitySchemes"]:["securityDefinitions"],s=o().getIn([...i,t]);return s?r({[t]:{value:n,schema:s.toJS()}}):null}},43962:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(86),o=n.n(r),a=n(76986),i=n.n(a),s=n(43393),l=n(90242),u=n(55812);const c={[u.SHOW_AUTH_POPUP]:(e,t)=>{let{payload:n}=t;return e.set("showDefinitions",n)},[u.AUTHORIZE]:(e,t)=>{var n;let{payload:r}=t,a=(0,s.fromJS)(r),i=e.get("authorized")||(0,s.Map)();return o()(n=a.entrySeq()).call(n,(t=>{let[n,r]=t;if(!(0,l.Wl)(r.getIn))return e.set("authorized",i);let o=r.getIn(["schema","type"]);if("apiKey"===o||"http"===o)i=i.set(n,r);else if("basic"===o){let e=r.getIn(["value","username"]),t=r.getIn(["value","password"]);i=i.setIn([n,"value"],{username:e,header:"Basic "+(0,l.r3)(e+":"+t)}),i=i.setIn([n,"schema"],r.get("schema"))}})),e.set("authorized",i)},[u.AUTHORIZE_OAUTH2]:(e,t)=>{let n,{payload:r}=t,{auth:o,token:a}=r;o.token=i()({},a),n=(0,s.fromJS)(o);let l=e.get("authorized")||(0,s.Map)();return l=l.set(n.get("name"),n),e.set("authorized",l)},[u.LOGOUT]:(e,t)=>{let{payload:n}=t,r=e.get("authorized").withMutations((e=>{o()(n).call(n,(t=>{e.delete(t)}))}));return e.set("authorized",r)},[u.CONFIGURE_AUTH]:(e,t)=>{let{payload:n}=t;return e.set("configs",n)},[u.RESTORE_AUTHORIZATION]:(e,t)=>{let{payload:n}=t;return e.set("authorized",(0,s.fromJS)(n.authorized))}}},60035:(e,t,n)=>{"use strict";n.r(t),n.d(t,{authorized:()=>x,definitionsForRequirements:()=>E,definitionsToAuthorize:()=>b,getConfigs:()=>S,getDefinitionsByNames:()=>w,isAuthorized:()=>_,shownDefinitions:()=>v});var r=n(86),o=n.n(r),a=n(51679),i=n.n(a),s=n(14418),l=n.n(s),u=n(11882),c=n.n(u),p=n(97606),f=n.n(p),h=n(28222),d=n.n(h),m=n(20573),g=n(43393);const y=e=>e,v=(0,m.P1)(y,(e=>e.get("showDefinitions"))),b=(0,m.P1)(y,(()=>e=>{var t;let{specSelectors:n}=e,r=n.securityDefinitions()||(0,g.Map)({}),a=(0,g.List)();return o()(t=r.entrySeq()).call(t,(e=>{let[t,n]=e,r=(0,g.Map)();r=r.set(t,n),a=a.push(r)})),a})),w=(e,t)=>e=>{var n;let{specSelectors:r}=e;console.warn("WARNING: getDefinitionsByNames is deprecated and will be removed in the next major version.");let a=r.securityDefinitions(),i=(0,g.List)();return o()(n=t.valueSeq()).call(n,(e=>{var t;let n=(0,g.Map)();o()(t=e.entrySeq()).call(t,(e=>{let t,[r,i]=e,s=a.get(r);var l;"oauth2"===s.get("type")&&i.size&&(t=s.get("scopes"),o()(l=t.keySeq()).call(l,(e=>{i.contains(e)||(t=t.delete(e))})),s=s.set("allowedScopes",t));n=n.set(r,s)})),i=i.push(n)})),i},E=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:(0,g.List)();return e=>{let{authSelectors:n}=e;const r=n.definitionsToAuthorize()||(0,g.List)();let a=(0,g.List)();return o()(r).call(r,(e=>{let n=i()(t).call(t,(t=>t.get(e.keySeq().first())));n&&(o()(e).call(e,((t,r)=>{if("oauth2"===t.get("type")){const i=n.get(r);let s=t.get("scopes");var a;if(g.List.isList(i)&&g.Map.isMap(s))o()(a=s.keySeq()).call(a,(e=>{i.contains(e)||(s=s.delete(e))})),e=e.set(r,t.set("scopes",s))}})),a=a.push(e))})),a}},x=(0,m.P1)(y,(e=>e.get("authorized")||(0,g.Map)())),_=(e,t)=>e=>{var n;let{authSelectors:r}=e,o=r.authorized();return g.List.isList(t)?!!l()(n=t.toJS()).call(n,(e=>{var t,n;return-1===c()(t=f()(n=d()(e)).call(n,(e=>!!o.get(e)))).call(t,!1)})).length:null},S=(0,m.P1)(y,(e=>e.get("configs")))},48302:(e,t,n)=>{"use strict";n.r(t),n.d(t,{execute:()=>r});const r=(e,t)=>{let{authSelectors:n,specSelectors:r}=t;return t=>{let{path:o,method:a,operation:i,extras:s}=t,l={authorized:n.authorized()&&n.authorized().toJS(),definitions:r.securityDefinitions()&&r.securityDefinitions().toJS(),specSecurity:r.security()&&r.security().toJS()};return e({path:o,method:a,operation:i,securities:l,...s})}}},70714:(e,t,n)=>{"use strict";n.r(t),n.d(t,{TOGGLE_CONFIGS:()=>o,UPDATE_CONFIGS:()=>r,loaded:()=>s,toggle:()=>i,update:()=>a});const r="configs_update",o="configs_toggle";function a(e,t){return{type:r,payload:{[e]:t}}}function i(e){return{type:o,payload:e}}const s=()=>e=>{let{getConfigs:t,authActions:n}=e;if(t().persistAuthorization){const e=localStorage.getItem("authorized");e&&n.restoreAuthorization({authorized:JSON.parse(e)})}}},92256:(e,t,n)=>{"use strict";n.r(t),n.d(t,{parseYamlConfig:()=>o});var r=n(1272);const o=(e,t)=>{try{return r.ZP.load(e)}catch(e){return t&&t.errActions.newThrownErr(new Error(e)),{}}}},46709:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(92256),o=n(70714),a=n(22698),i=n(69018),s=n(37743);const l={getLocalConfig:()=>(0,r.parseYamlConfig)('---\nurl: "https://petstore.swagger.io/v2/swagger.json"\ndom_id: "#swagger-ui"\nvalidatorUrl: "https://validator.swagger.io/validator"\n')};function u(){return{statePlugins:{spec:{actions:a,selectors:l},configs:{reducers:s.default,actions:o,selectors:i}}}}},37743:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(43393),o=n(70714);const a={[o.UPDATE_CONFIGS]:(e,t)=>e.merge((0,r.fromJS)(t.payload)),[o.TOGGLE_CONFIGS]:(e,t)=>{const n=t.payload,r=e.get(n);return e.set(n,!r)}}},69018:(e,t,n)=>{"use strict";n.r(t),n.d(t,{get:()=>a});var r=n(58309),o=n.n(r);const a=(e,t)=>e.getIn(o()(t)?t:[t])},22698:(e,t,n)=>{"use strict";n.r(t),n.d(t,{downloadConfig:()=>o,getConfigByUrl:()=>a});var r=n(92256);const o=e=>t=>{const{fn:{fetch:n}}=t;return n(e)},a=(e,t)=>n=>{let{specActions:o}=n;if(e)return o.downloadConfig(e).then(a,a);function a(n){n instanceof Error||n.status>=400?(o.updateLoadingStatus("failedConfig"),o.updateLoadingStatus("failedConfig"),o.updateUrl(""),console.error(n.statusText+" "+e.url),t(null)):t((0,r.parseYamlConfig)(n.text))}}},31970:(e,t,n)=>{"use strict";n.r(t),n.d(t,{setHash:()=>r});const r=e=>e?history.pushState(null,null,`#${e}`):window.location.hash=""},34980:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(41599),o=n(60877),a=n(34584);function i(){return[r.default,{statePlugins:{configs:{wrapActions:{loaded:(e,t)=>function(){e(...arguments);const n=decodeURIComponent(window.location.hash);t.layoutActions.parseDeepLinkHash(n)}}}},wrapComponents:{operation:o.default,OperationTag:a.default}}]}},41599:(e,t,n)=>{"use strict";n.r(t),n.d(t,{clearScrollTo:()=>S,default:()=>A,parseDeepLinkHash:()=>E,readyToScroll:()=>x,scrollTo:()=>w,scrollToElement:()=>_,show:()=>b});var r=n(58309),o=n.n(r),a=n(24278),i=n.n(a),s=n(97606),l=n.n(s),u=n(11882),c=n.n(u),p=n(31970),f=n(45172),h=n.n(f),d=n(90242),m=n(43393),g=n.n(m);const y="layout_scroll_to",v="layout_clear_scroll",b=(e,t)=>{let{getConfigs:n,layoutSelectors:r}=t;return function(){for(var t=arguments.length,a=new Array(t),i=0;i({type:y,payload:o()(e)?e:[e]}),E=e=>t=>{let{layoutActions:n,layoutSelectors:r,getConfigs:o}=t;if(o().deepLinking&&e){var a;let t=i()(e).call(e,1);"!"===t[0]&&(t=i()(t).call(t,1)),"/"===t[0]&&(t=i()(t).call(t,1));const o=l()(a=t.split("/")).call(a,(e=>e||"")),s=r.isShownKeyFromUrlHashArray(o),[u,p="",f=""]=s;if("operations"===u){const e=r.isShownKeyFromUrlHashArray([p]);c()(p).call(p,"_")>-1&&(console.warn("Warning: escaping deep link whitespace with `_` will be unsupported in v4.0, use `%20` instead."),n.show(l()(e).call(e,(e=>e.replace(/_/g," "))),!0)),n.show(e,!0)}(c()(p).call(p,"_")>-1||c()(f).call(f,"_")>-1)&&(console.warn("Warning: escaping deep link whitespace with `_` will be unsupported in v4.0, use `%20` instead."),n.show(l()(s).call(s,(e=>e.replace(/_/g," "))),!0)),n.show(s,!0),n.scrollTo(s)}},x=(e,t)=>n=>{const r=n.layoutSelectors.getScrollToKey();g().is(r,(0,m.fromJS)(e))&&(n.layoutActions.scrollToElement(t),n.layoutActions.clearScrollTo())},_=(e,t)=>n=>{try{t=t||n.fn.getScrollParent(e),h().createScroller(t).to(e)}catch(e){console.error(e)}},S=()=>({type:v});const A={fn:{getScrollParent:function(e,t){const n=document.documentElement;let r=getComputedStyle(e);const o="absolute"===r.position,a=t?/(auto|scroll|hidden)/:/(auto|scroll)/;if("fixed"===r.position)return n;for(let t=e;t=t.parentElement;)if(r=getComputedStyle(t),(!o||"static"!==r.position)&&a.test(r.overflow+r.overflowY+r.overflowX))return t;return n}},statePlugins:{layout:{actions:{scrollToElement:_,scrollTo:w,clearScrollTo:S,readyToScroll:x,parseDeepLinkHash:E},selectors:{getScrollToKey:e=>e.get("scrollToKey"),isShownKeyFromUrlHashArray(e,t){const[n,r]=t;return r?["operations",n,r]:n?["operations-tag",n]:[]},urlHashArrayFromIsShownKey(e,t){let[n,r,o]=t;return"operations"==n?[r,o]:"operations-tag"==n?[r]:[]}},reducers:{[y]:(e,t)=>e.set("scrollToKey",g().fromJS(t.payload)),[v]:e=>e.delete("scrollToKey")},wrapActions:{show:b}}}}},34584:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(61125),o=n.n(r),a=n(67294);const i=(e,t)=>class extends a.Component{constructor(){super(...arguments),o()(this,"onLoad",(e=>{const{tag:n}=this.props,r=["operations-tag",n];t.layoutActions.readyToScroll(r,e)}))}render(){return a.createElement("span",{ref:this.onLoad},a.createElement(e,this.props))}}},60877:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(61125),o=n.n(r),a=n(67294);n(23930);const i=(e,t)=>class extends a.Component{constructor(){super(...arguments),o()(this,"onLoad",(e=>{const{operation:n}=this.props,{tag:r,operationId:o}=n.toObject();let{isShownKey:a}=n.toObject();a=a||["operations",r,o],t.layoutActions.readyToScroll(a,e)}))}render(){return a.createElement("span",{ref:this.onLoad},a.createElement(e,this.props))}}},48011:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>d});var r=n(76986),o=n.n(r),a=n(63460),i=n.n(a),s=n(11882),l=n.n(s),u=n(35627),c=n.n(u),p=n(20573),f=n(43393),h=n(27504);function d(e){let{fn:t}=e;return{statePlugins:{spec:{actions:{download:e=>n=>{let{errActions:r,specSelectors:a,specActions:s,getConfigs:l}=n,{fetch:u}=t;const c=l();function p(t){if(t instanceof Error||t.status>=400)return s.updateLoadingStatus("failed"),r.newThrownErr(o()(new Error((t.message||t.statusText)+" "+e),{source:"fetch"})),void(!t.status&&t instanceof Error&&function(){try{let t;if("URL"in h.Z?t=new(i())(e):(t=document.createElement("a"),t.href=e),"https:"!==t.protocol&&"https:"===h.Z.location.protocol){const e=o()(new Error(`Possible mixed-content issue? The page was loaded over https:// but a ${t.protocol}// URL was specified. Check that you are not attempting to load mixed content.`),{source:"fetch"});return void r.newThrownErr(e)}if(t.origin!==h.Z.location.origin){const e=o()(new Error(`Possible cross-origin (CORS) issue? The URL origin (${t.origin}) does not match the page (${h.Z.location.origin}). Check the server returns the correct 'Access-Control-Allow-*' headers.`),{source:"fetch"});r.newThrownErr(e)}}catch(e){return}}());s.updateLoadingStatus("success"),s.updateSpec(t.text),a.url()!==e&&s.updateUrl(e)}e=e||a.url(),s.updateLoadingStatus("loading"),r.clear({source:"fetch"}),u({url:e,loadSpec:!0,requestInterceptor:c.requestInterceptor||(e=>e),responseInterceptor:c.responseInterceptor||(e=>e),credentials:"same-origin",headers:{Accept:"application/json,*/*"}}).then(p,p)},updateLoadingStatus:e=>{let t=[null,"loading","failed","success","failedConfig"];return-1===l()(t).call(t,e)&&console.error(`Error: ${e} is not one of ${c()(t)}`),{type:"spec_update_loading_status",payload:e}}},reducers:{spec_update_loading_status:(e,t)=>"string"==typeof t.payload?e.set("loadingStatus",t.payload):e},selectors:{loadingStatus:(0,p.P1)((e=>e||(0,f.Map)()),(e=>e.get("loadingStatus")||null))}}}}}},34966:(e,t,n)=>{"use strict";n.r(t),n.d(t,{CLEAR:()=>u,CLEAR_BY:()=>c,NEW_AUTH_ERR:()=>l,NEW_SPEC_ERR:()=>i,NEW_SPEC_ERR_BATCH:()=>s,NEW_THROWN_ERR:()=>o,NEW_THROWN_ERR_BATCH:()=>a,clear:()=>g,clearBy:()=>y,newAuthErr:()=>m,newSpecErr:()=>h,newSpecErrBatch:()=>d,newThrownErr:()=>p,newThrownErrBatch:()=>f});var r=n(7710);const o="err_new_thrown_err",a="err_new_thrown_err_batch",i="err_new_spec_err",s="err_new_spec_err_batch",l="err_new_auth_err",u="err_clear",c="err_clear_by";function p(e){return{type:o,payload:(0,r.serializeError)(e)}}function f(e){return{type:a,payload:e}}function h(e){return{type:i,payload:e}}function d(e){return{type:s,payload:e}}function m(e){return{type:l,payload:e}}function g(){return{type:u,payload:arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}}}function y(){return{type:c,payload:arguments.length>0&&void 0!==arguments[0]?arguments[0]:()=>!0}}},56982:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(14418),o=n.n(r),a=n(97606),i=n.n(a),s=n(54061),l=n.n(s);const u=[n(2392),n(21835)];function c(e){var t;let n={jsSpec:{}},r=l()(u,((e,t)=>{try{let r=t.transform(e,n);return o()(r).call(r,(e=>!!e))}catch(t){return console.error("Transformer error:",t),e}}),e);return i()(t=o()(r).call(r,(e=>!!e))).call(t,(e=>(!e.get("line")&&e.get("path"),e)))}},2392:(e,t,n)=>{"use strict";n.r(t),n.d(t,{transform:()=>p});var r=n(97606),o=n.n(r),a=n(11882),i=n.n(a),s=n(24278),l=n.n(s),u=n(24282),c=n.n(u);function p(e){return o()(e).call(e,(e=>{var t;let n="is not of a type(s)",r=i()(t=e.get("message")).call(t,n);if(r>-1){var o,a;let t=l()(o=e.get("message")).call(o,r+n.length).split(",");return e.set("message",l()(a=e.get("message")).call(a,0,r)+function(e){return c()(e).call(e,((e,t,n,r)=>n===r.length-1&&r.length>1?e+"or "+t:r[n+1]&&r.length>2?e+t+", ":r[n+1]?e+t+" ":e+t),"should be a")}(t))}return e}))}},21835:(e,t,n)=>{"use strict";n.r(t),n.d(t,{transform:()=>r});n(97606),n(11882),n(27361),n(43393);function r(e,t){let{jsSpec:n}=t;return e}},77793:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(93527),o=n(34966),a=n(87667);function i(e){return{statePlugins:{err:{reducers:(0,r.default)(e),actions:o,selectors:a}}}}},93527:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>y});var r=n(76986),o=n.n(r),a=n(97606),i=n.n(a),s=n(39022),l=n.n(s),u=n(14418),c=n.n(u),p=n(2250),f=n.n(p),h=n(34966),d=n(43393),m=n(56982);let g={line:0,level:"error",message:"Unknown error"};function y(){return{[h.NEW_THROWN_ERR]:(e,t)=>{let{payload:n}=t,r=o()(g,n,{type:"thrown"});return e.update("errors",(e=>(e||(0,d.List)()).push((0,d.fromJS)(r)))).update("errors",(e=>(0,m.default)(e)))},[h.NEW_THROWN_ERR_BATCH]:(e,t)=>{let{payload:n}=t;return n=i()(n).call(n,(e=>(0,d.fromJS)(o()(g,e,{type:"thrown"})))),e.update("errors",(e=>{var t;return l()(t=e||(0,d.List)()).call(t,(0,d.fromJS)(n))})).update("errors",(e=>(0,m.default)(e)))},[h.NEW_SPEC_ERR]:(e,t)=>{let{payload:n}=t,r=(0,d.fromJS)(n);return r=r.set("type","spec"),e.update("errors",(e=>(e||(0,d.List)()).push((0,d.fromJS)(r)).sortBy((e=>e.get("line"))))).update("errors",(e=>(0,m.default)(e)))},[h.NEW_SPEC_ERR_BATCH]:(e,t)=>{let{payload:n}=t;return n=i()(n).call(n,(e=>(0,d.fromJS)(o()(g,e,{type:"spec"})))),e.update("errors",(e=>{var t;return l()(t=e||(0,d.List)()).call(t,(0,d.fromJS)(n))})).update("errors",(e=>(0,m.default)(e)))},[h.NEW_AUTH_ERR]:(e,t)=>{let{payload:n}=t,r=(0,d.fromJS)(o()({},n));return r=r.set("type","auth"),e.update("errors",(e=>(e||(0,d.List)()).push((0,d.fromJS)(r)))).update("errors",(e=>(0,m.default)(e)))},[h.CLEAR]:(e,t)=>{var n;let{payload:r}=t;if(!r||!e.get("errors"))return e;let o=c()(n=e.get("errors")).call(n,(e=>{var t;return f()(t=e.keySeq()).call(t,(t=>{const n=e.get(t),o=r[t];return!o||n!==o}))}));return e.merge({errors:o})},[h.CLEAR_BY]:(e,t)=>{var n;let{payload:r}=t;if(!r||"function"!=typeof r)return e;let o=c()(n=e.get("errors")).call(n,(e=>r(e)));return e.merge({errors:o})}}}},87667:(e,t,n)=>{"use strict";n.r(t),n.d(t,{allErrors:()=>a,lastError:()=>i});var r=n(43393),o=n(20573);const a=(0,o.P1)((e=>e),(e=>e.get("errors",(0,r.List)()))),i=(0,o.P1)(a,(e=>e.last()))},49978:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(4309);function o(){return{fn:{opsFilter:r.default}}}},4309:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(14418),o=n.n(r),a=n(11882),i=n.n(a);function s(e,t){return o()(e).call(e,((e,n)=>-1!==i()(n).call(n,t)))}},25474:(e,t,n)=>{"use strict";n.r(t),n.d(t,{SHOW:()=>s,UPDATE_FILTER:()=>a,UPDATE_LAYOUT:()=>o,UPDATE_MODE:()=>i,changeMode:()=>p,show:()=>c,updateFilter:()=>u,updateLayout:()=>l});var r=n(90242);const o="layout_update_layout",a="layout_update_filter",i="layout_update_mode",s="layout_show";function l(e){return{type:o,payload:e}}function u(e){return{type:a,payload:e}}function c(e){let t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return e=(0,r.AF)(e),{type:s,payload:{thing:e,shown:t}}}function p(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return e=(0,r.AF)(e),{type:i,payload:{thing:e,mode:t}}}},26821:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(5672),o=n(25474),a=n(4400),i=n(28989);function s(){return{statePlugins:{layout:{reducers:r.default,actions:o,selectors:a},spec:{wrapSelectors:i}}}}},5672:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(39022),o=n.n(r),a=n(43393),i=n(25474);const s={[i.UPDATE_LAYOUT]:(e,t)=>e.set("layout",t.payload),[i.UPDATE_FILTER]:(e,t)=>e.set("filter",t.payload),[i.SHOW]:(e,t)=>{const n=t.payload.shown,r=(0,a.fromJS)(t.payload.thing);return e.update("shown",(0,a.fromJS)({}),(e=>e.set(r,n)))},[i.UPDATE_MODE]:(e,t)=>{var n;let r=t.payload.thing,a=t.payload.mode;return e.setIn(o()(n=["modes"]).call(n,r),(a||"")+"")}}},4400:(e,t,n)=>{"use strict";n.r(t),n.d(t,{current:()=>i,currentFilter:()=>s,isShown:()=>l,showSummary:()=>c,whatMode:()=>u});var r=n(20573),o=n(90242),a=n(43393);const i=e=>e.get("layout"),s=e=>e.get("filter"),l=(e,t,n)=>(t=(0,o.AF)(t),e.get("shown",(0,a.fromJS)({})).get((0,a.fromJS)(t),n)),u=function(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return t=(0,o.AF)(t),e.getIn(["modes",...t],n)},c=(0,r.P1)((e=>e),(e=>!l(e,"editor")))},28989:(e,t,n)=>{"use strict";n.r(t),n.d(t,{taggedOperations:()=>a});var r=n(24278),o=n.n(r);const a=(e,t)=>function(n){for(var r=arguments.length,a=new Array(r>1?r-1:0),i=1;i=0&&(s=o()(s).call(s,0,f)),s}},9150:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(11189),o=n.n(r);function a(e){let{configs:t}=e;const n={debug:0,info:1,log:2,warn:3,error:4},r=e=>n[e]||-1;let{logLevel:a}=t,i=r(a);function s(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),o=1;o=i&&console[e](...n)}return s.warn=o()(s).call(s,null,"warn"),s.error=o()(s).call(s,null,"error"),s.info=o()(s).call(s,null,"info"),s.debug=o()(s).call(s,null,"debug"),{rootInjects:{log:s}}}},67002:(e,t,n)=>{"use strict";n.r(t),n.d(t,{CLEAR_REQUEST_BODY_VALIDATE_ERROR:()=>f,CLEAR_REQUEST_BODY_VALUE:()=>h,SET_REQUEST_BODY_VALIDATE_ERROR:()=>p,UPDATE_ACTIVE_EXAMPLES_MEMBER:()=>s,UPDATE_REQUEST_BODY_INCLUSION:()=>i,UPDATE_REQUEST_BODY_VALUE:()=>o,UPDATE_REQUEST_BODY_VALUE_RETAIN_FLAG:()=>a,UPDATE_REQUEST_CONTENT_TYPE:()=>l,UPDATE_RESPONSE_CONTENT_TYPE:()=>u,UPDATE_SELECTED_SERVER:()=>r,UPDATE_SERVER_VARIABLE_VALUE:()=>c,clearRequestBodyValidateError:()=>_,clearRequestBodyValue:()=>A,initRequestBodyValidateError:()=>S,setActiveExamplesMember:()=>v,setRequestBodyInclusion:()=>y,setRequestBodyValidateError:()=>x,setRequestBodyValue:()=>m,setRequestContentType:()=>b,setResponseContentType:()=>w,setRetainRequestBodyValueFlag:()=>g,setSelectedServer:()=>d,setServerVariableValue:()=>E});const r="oas3_set_servers",o="oas3_set_request_body_value",a="oas3_set_request_body_retain_flag",i="oas3_set_request_body_inclusion",s="oas3_set_active_examples_member",l="oas3_set_request_content_type",u="oas3_set_response_content_type",c="oas3_set_server_variable_value",p="oas3_set_request_body_validate_error",f="oas3_clear_request_body_validate_error",h="oas3_clear_request_body_value";function d(e,t){return{type:r,payload:{selectedServerUrl:e,namespace:t}}}function m(e){let{value:t,pathMethod:n}=e;return{type:o,payload:{value:t,pathMethod:n}}}const g=e=>{let{value:t,pathMethod:n}=e;return{type:a,payload:{value:t,pathMethod:n}}};function y(e){let{value:t,pathMethod:n,name:r}=e;return{type:i,payload:{value:t,pathMethod:n,name:r}}}function v(e){let{name:t,pathMethod:n,contextType:r,contextName:o}=e;return{type:s,payload:{name:t,pathMethod:n,contextType:r,contextName:o}}}function b(e){let{value:t,pathMethod:n}=e;return{type:l,payload:{value:t,pathMethod:n}}}function w(e){let{value:t,path:n,method:r}=e;return{type:u,payload:{value:t,path:n,method:r}}}function E(e){let{server:t,namespace:n,key:r,val:o}=e;return{type:c,payload:{server:t,namespace:n,key:r,val:o}}}const x=e=>{let{path:t,method:n,validationErrors:r}=e;return{type:p,payload:{path:t,method:n,validationErrors:r}}},_=e=>{let{path:t,method:n}=e;return{type:f,payload:{path:t,method:n}}},S=e=>{let{pathMethod:t}=e;return{type:f,payload:{path:t[0],method:t[1]}}},A=e=>{let{pathMethod:t}=e;return{type:h,payload:{pathMethod:t}}}},73723:(e,t,n)=>{"use strict";n.r(t),n.d(t,{definitionsToAuthorize:()=>f});var r=n(86),o=n.n(r),a=n(14418),i=n.n(a),s=n(24282),l=n.n(s),u=n(20573),c=n(43393),p=n(7779);const f=(h=(0,u.P1)((e=>e),(e=>{let{specSelectors:t}=e;return t.securityDefinitions()}),((e,t)=>{var n;let r=(0,c.List)();return t?(o()(n=t.entrySeq()).call(n,(e=>{let[t,n]=e;const a=n.get("type");var s;if("oauth2"===a&&o()(s=n.get("flows").entrySeq()).call(s,(e=>{let[o,a]=e,s=(0,c.fromJS)({flow:o,authorizationUrl:a.get("authorizationUrl"),tokenUrl:a.get("tokenUrl"),scopes:a.get("scopes"),type:n.get("type"),description:n.get("description")});r=r.push(new c.Map({[t]:i()(s).call(s,(e=>void 0!==e))}))})),"http"!==a&&"apiKey"!==a||(r=r.push(new c.Map({[t]:n}))),"openIdConnect"===a&&n.get("openIdConnectData")){let e=n.get("openIdConnectData"),a=e.get("grant_types_supported")||["authorization_code","implicit"];o()(a).call(a,(o=>{var a;let s=e.get("scopes_supported")&&l()(a=e.get("scopes_supported")).call(a,((e,t)=>e.set(t,"")),new c.Map),u=(0,c.fromJS)({flow:o,authorizationUrl:e.get("authorization_endpoint"),tokenUrl:e.get("token_endpoint"),scopes:s,type:"oauth2",openIdConnectUrl:n.get("openIdConnectUrl")});r=r.push(new c.Map({[t]:i()(u).call(u,(e=>void 0!==e))}))}))}})),r):r})),(e,t)=>function(){const n=t.getSystem().specSelectors.specJson();for(var r=arguments.length,o=new Array(r),a=0;a{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(23101),o=n.n(r),a=n(97606),i=n.n(a),s=n(67294),l=(n(23930),n(43393));const u=e=>{var t;let{callbacks:n,getComponent:r,specPath:a}=e;const u=r("OperationContainer",!0);if(!n)return s.createElement("span",null,"No callbacks");let c=i()(t=n.entrySeq()).call(t,(t=>{var n;let[r,c]=t;return s.createElement("div",{key:r},s.createElement("h2",null,r),i()(n=c.entrySeq()).call(n,(t=>{var n;let[c,p]=t;return"$$ref"===c?null:s.createElement("div",{key:c},i()(n=p.entrySeq()).call(n,(t=>{let[n,i]=t;if("$$ref"===n)return null;let p=(0,l.fromJS)({operation:i});return s.createElement(u,o()({},e,{op:p,key:n,tag:"",method:n,path:c,specPath:a.push(r,c,n),allowTryItOut:!1}))})))})))}));return s.createElement("div",null,c)}},86775:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>f});var r=n(61125),o=n.n(r),a=n(76986),i=n.n(a),s=n(14418),l=n.n(s),u=n(97606),c=n.n(u),p=n(67294);class f extends p.Component{constructor(e,t){super(e,t),o()(this,"onChange",(e=>{let{onChange:t}=this.props,{value:n,name:r}=e.target,o=i()({},this.state.value);r?o[r]=n:o=n,this.setState({value:o},(()=>t(this.state)))}));let{name:n,schema:r}=this.props,a=this.getValue();this.state={name:n,schema:r,value:a}}getValue(){let{name:e,authorized:t}=this.props;return t&&t.getIn([e,"value"])}render(){var e;let{schema:t,getComponent:n,errSelectors:r,name:o}=this.props;const a=n("Input"),i=n("Row"),s=n("Col"),u=n("authError"),f=n("Markdown",!0),h=n("JumpToPath",!0),d=(t.get("scheme")||"").toLowerCase();let m=this.getValue(),g=l()(e=r.allErrors()).call(e,(e=>e.get("authId")===o));if("basic"===d){var y;let e=m?m.get("username"):null;return p.createElement("div",null,p.createElement("h4",null,p.createElement("code",null,o||t.get("name")),"  (http, Basic)",p.createElement(h,{path:["securityDefinitions",o]})),e&&p.createElement("h6",null,"Authorized"),p.createElement(i,null,p.createElement(f,{source:t.get("description")})),p.createElement(i,null,p.createElement("label",null,"Username:"),e?p.createElement("code",null," ",e," "):p.createElement(s,null,p.createElement(a,{type:"text",required:"required",name:"username","aria-label":"auth-basic-username",onChange:this.onChange,autoFocus:!0}))),p.createElement(i,null,p.createElement("label",null,"Password:"),e?p.createElement("code",null," ****** "):p.createElement(s,null,p.createElement(a,{autoComplete:"new-password",name:"password",type:"password","aria-label":"auth-basic-password",onChange:this.onChange}))),c()(y=g.valueSeq()).call(y,((e,t)=>p.createElement(u,{error:e,key:t}))))}var v;return"bearer"===d?p.createElement("div",null,p.createElement("h4",null,p.createElement("code",null,o||t.get("name")),"  (http, Bearer)",p.createElement(h,{path:["securityDefinitions",o]})),m&&p.createElement("h6",null,"Authorized"),p.createElement(i,null,p.createElement(f,{source:t.get("description")})),p.createElement(i,null,p.createElement("label",null,"Value:"),m?p.createElement("code",null," ****** "):p.createElement(s,null,p.createElement(a,{type:"text","aria-label":"auth-bearer-value",onChange:this.onChange,autoFocus:!0}))),c()(v=g.valueSeq()).call(v,((e,t)=>p.createElement(u,{error:e,key:t})))):p.createElement("div",null,p.createElement("em",null,p.createElement("b",null,o)," HTTP authentication: unsupported scheme ",`'${d}'`))}}},76467:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(33427),o=n(42458),a=n(15757),i=n(56617),s=n(9928),l=n(45327),u=n(86775),c=n(96796);const p={Callbacks:r.default,HttpAuth:u.default,RequestBody:o.default,Servers:i.default,ServersContainer:s.default,RequestBodyEditor:l.default,OperationServers:c.default,operationLink:a.default}},15757:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(35627),o=n.n(r),a=n(97606),i=n.n(a),s=n(67294);n(23930);class l extends s.Component{render(){const{link:e,name:t,getComponent:n}=this.props,r=n("Markdown",!0);let a=e.get("operationId")||e.get("operationRef"),l=e.get("parameters")&&e.get("parameters").toJS(),u=e.get("description");return s.createElement("div",{className:"operation-link"},s.createElement("div",{className:"description"},s.createElement("b",null,s.createElement("code",null,t)),u?s.createElement(r,{source:u}):null),s.createElement("pre",null,"Operation `",a,"`",s.createElement("br",null),s.createElement("br",null),"Parameters ",function(e,t){var n;if("string"!=typeof t)return"";return i()(n=t.split("\n")).call(n,((t,n)=>n>0?Array(e+1).join(" ")+t:t)).join("\n")}(0,o()(l,null,2))||"{}",s.createElement("br",null)))}}const u=l},96796:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(61125),o=n.n(r),a=n(67294);n(23930);class i extends a.Component{constructor(){super(...arguments),o()(this,"setSelectedServer",(e=>{const{path:t,method:n}=this.props;return this.forceUpdate(),this.props.setSelectedServer(e,`${t}:${n}`)})),o()(this,"setServerVariableValue",(e=>{const{path:t,method:n}=this.props;return this.forceUpdate(),this.props.setServerVariableValue({...e,namespace:`${t}:${n}`})})),o()(this,"getSelectedServer",(()=>{const{path:e,method:t}=this.props;return this.props.getSelectedServer(`${e}:${t}`)})),o()(this,"getServerVariable",((e,t)=>{const{path:n,method:r}=this.props;return this.props.getServerVariable({namespace:`${n}:${r}`,server:e},t)})),o()(this,"getEffectiveServerValue",(e=>{const{path:t,method:n}=this.props;return this.props.getEffectiveServerValue({server:e,namespace:`${t}:${n}`})}))}render(){const{operationServers:e,pathServers:t,getComponent:n}=this.props;if(!e&&!t)return null;const r=n("Servers"),o=e||t,i=e?"operation":"path";return a.createElement("div",{className:"opblock-section operation-servers"},a.createElement("div",{className:"opblock-section-header"},a.createElement("div",{className:"tab-header"},a.createElement("h4",{className:"opblock-title"},"Servers"))),a.createElement("div",{className:"opblock-description-wrapper"},a.createElement("h4",{className:"message"},"These ",i,"-level options override the global server options."),a.createElement(r,{servers:o,currentServer:this.getSelectedServer(),setSelectedServer:this.setSelectedServer,setServerVariableValue:this.setServerVariableValue,getServerVariable:this.getServerVariable,getEffectiveServerValue:this.getEffectiveServerValue})))}}},45327:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(61125),o=n.n(r),a=n(67294),i=n(94184),s=n.n(i),l=n(90242);const u=Function.prototype;class c extends a.PureComponent{constructor(e,t){super(e,t),o()(this,"applyDefaultValue",(e=>{const{onChange:t,defaultValue:n}=e||this.props;return this.setState({value:n}),t(n)})),o()(this,"onChange",(e=>{this.props.onChange((0,l.Pz)(e))})),o()(this,"onDomChange",(e=>{const t=e.target.value;this.setState({value:t},(()=>this.onChange(t)))})),this.state={value:(0,l.Pz)(e.value)||e.defaultValue},e.onChange(e.value)}UNSAFE_componentWillReceiveProps(e){this.props.value!==e.value&&e.value!==this.state.value&&this.setState({value:(0,l.Pz)(e.value)}),!e.value&&e.defaultValue&&this.state.value&&this.applyDefaultValue(e)}render(){let{getComponent:e,errors:t}=this.props,{value:n}=this.state,r=t.size>0;const o=e("TextArea");return a.createElement("div",{className:"body-param"},a.createElement(o,{className:s()("body-param__text",{invalid:r}),title:t.size?t.join(", "):"",value:n,onChange:this.onDomChange}))}}o()(c,"defaultProps",{onChange:u,userHasEditedBody:!1})},42458:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>g,getDefaultRequestBodyValue:()=>m});var r=n(97606),o=n.n(r),a=n(11882),i=n.n(a),s=n(58118),l=n.n(s),u=n(58309),c=n.n(u),p=n(67294),f=(n(23930),n(43393)),h=n(90242),d=n(2518);const m=(e,t,n)=>{const r=e.getIn(["content",t]),o=r.get("schema").toJS(),a=void 0!==r.get("examples"),i=r.get("example"),s=a?r.getIn(["examples",n,"value"]):i,l=(0,h.xi)(o,t,{includeWriteOnly:!0},s);return(0,h.Pz)(l)},g=e=>{let{userHasEditedBody:t,requestBody:n,requestBodyValue:r,requestBodyInclusionSetting:a,requestBodyErrors:s,getComponent:u,getConfigs:g,specSelectors:y,fn:v,contentType:b,isExecute:w,specPath:E,onChange:x,onChangeIncludeEmpty:_,activeExamplesKey:S,updateActiveExamplesKey:A,setRetainRequestBodyValueFlag:C}=e;const k=e=>{x(e.target.files[0])},O=e=>{let t={key:e,shouldDispatchInit:!1,defaultValue:!0};return"no value"===a.get(e,"no value")&&(t.shouldDispatchInit=!0),t},j=u("Markdown",!0),T=u("modelExample"),I=u("RequestBodyEditor"),N=u("highlightCode"),P=u("ExamplesSelectValueRetainer"),R=u("Example"),M=u("ParameterIncludeEmpty"),{showCommonExtensions:D}=g(),L=n&&n.get("description")||null,B=n&&n.get("content")||new f.OrderedMap;b=b||B.keySeq().first()||"";const F=B.get(b,(0,f.OrderedMap)()),U=F.get("schema",(0,f.OrderedMap)()),z=F.get("examples",null),q=null==z?void 0:o()(z).call(z,((e,t)=>{var r;const o=null===(r=e)||void 0===r?void 0:r.get("value",null);return o&&(e=e.set("value",m(n,b,t),o)),e}));if(s=f.List.isList(s)?s:(0,f.List)(),!F.size)return null;const $="object"===F.getIn(["schema","type"]),V="binary"===F.getIn(["schema","format"]),H="base64"===F.getIn(["schema","format"]);if("application/octet-stream"===b||0===i()(b).call(b,"image/")||0===i()(b).call(b,"audio/")||0===i()(b).call(b,"video/")||V||H){const e=u("Input");return w?p.createElement(e,{type:"file",onChange:k}):p.createElement("i",null,"Example values are not available for ",p.createElement("code",null,b)," media types.")}if($&&("application/x-www-form-urlencoded"===b||0===i()(b).call(b,"multipart/"))&&U.get("properties",(0,f.OrderedMap)()).size>0){var W;const e=u("JsonSchemaForm"),t=u("ParameterExt"),n=U.get("properties",(0,f.OrderedMap)());return r=f.Map.isMap(r)?r:(0,f.OrderedMap)(),p.createElement("div",{className:"table-container"},L&&p.createElement(j,{source:L}),p.createElement("table",null,p.createElement("tbody",null,f.Map.isMap(n)&&o()(W=n.entrySeq()).call(W,(n=>{var i,d;let[m,g]=n;if(g.get("readOnly"))return;let y=D?(0,h.po)(g):null;const b=l()(i=U.get("required",(0,f.List)())).call(i,m),E=g.get("type"),S=g.get("format"),A=g.get("description"),C=r.getIn([m,"value"]),k=r.getIn([m,"errors"])||s,T=a.get(m)||!1,I=g.has("default")||g.has("example")||g.hasIn(["items","example"])||g.hasIn(["items","default"]),N=g.has("enum")&&(1===g.get("enum").size||b),P=I||N;let R="";"array"!==E||P||(R=[]),("object"===E||P)&&(R=(0,h.xi)(g,!1,{includeWriteOnly:!0})),"string"!=typeof R&&"object"===E&&(R=(0,h.Pz)(R)),"string"==typeof R&&"array"===E&&(R=JSON.parse(R));const L="string"===E&&("binary"===S||"base64"===S);return p.createElement("tr",{key:m,className:"parameters","data-property-name":m},p.createElement("td",{className:"parameters-col_name"},p.createElement("div",{className:b?"parameter__name required":"parameter__name"},m,b?p.createElement("span",null," *"):null),p.createElement("div",{className:"parameter__type"},E,S&&p.createElement("span",{className:"prop-format"},"($",S,")"),D&&y.size?o()(d=y.entrySeq()).call(d,(e=>{let[n,r]=e;return p.createElement(t,{key:`${n}-${r}`,xKey:n,xVal:r})})):null),p.createElement("div",{className:"parameter__deprecated"},g.get("deprecated")?"deprecated":null)),p.createElement("td",{className:"parameters-col_description"},p.createElement(j,{source:A}),w?p.createElement("div",null,p.createElement(e,{fn:v,dispatchInitialValue:!L,schema:g,description:m,getComponent:u,value:void 0===C?R:C,required:b,errors:k,onChange:e=>{x(e,[m])}}),b?null:p.createElement(M,{onChange:e=>_(m,e),isIncluded:T,isIncludedOptions:O(m),isDisabled:c()(C)?0!==C.length:!(0,h.O2)(C)})):null))})))))}const J=m(n,b,S);let K=null;return(0,d.O)(J)&&(K="json"),p.createElement("div",null,L&&p.createElement(j,{source:L}),q?p.createElement(P,{userHasEditedBody:t,examples:q,currentKey:S,currentUserInputValue:r,onSelect:e=>{A(e)},updateValue:x,defaultToFirstExample:!0,getComponent:u,setRetainRequestBodyValueFlag:C}):null,w?p.createElement("div",null,p.createElement(I,{value:r,errors:s,defaultValue:J,onChange:x,getComponent:u})):p.createElement(T,{getComponent:u,getConfigs:g,specSelectors:y,expandDepth:1,isExecute:w,schema:F.get("schema"),specPath:E.push("content",b),example:p.createElement(N,{className:"body-param__example",getConfigs:g,language:K,value:(0,h.Pz)(r)||J}),includeWriteOnly:!0}),q?p.createElement(R,{example:q.get(S),getComponent:u,getConfigs:g}):null)}},9928:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);class o extends r.Component{render(){const{specSelectors:e,oas3Selectors:t,oas3Actions:n,getComponent:o}=this.props,a=e.servers(),i=o("Servers");return a&&a.size?r.createElement("div",null,r.createElement("span",{className:"servers-title"},"Servers"),r.createElement(i,{servers:a,currentServer:t.selectedServer(),setSelectedServer:n.setSelectedServer,setServerVariableValue:n.setServerVariableValue,getServerVariable:t.serverVariableValue,getEffectiveServerValue:t.serverEffectiveValue})):null}}},56617:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(61125),o=n.n(r),a=n(51679),i=n.n(a),s=n(97606),l=n.n(s),u=n(67294),c=n(43393);n(23930);class p extends u.Component{constructor(){super(...arguments),o()(this,"onServerChange",(e=>{this.setServer(e.target.value)})),o()(this,"onServerVariableValueChange",(e=>{let{setServerVariableValue:t,currentServer:n}=this.props,r=e.target.getAttribute("data-variable"),o=e.target.value;"function"==typeof t&&t({server:n,key:r,val:o})})),o()(this,"setServer",(e=>{let{setSelectedServer:t}=this.props;t(e)}))}componentDidMount(){var e;let{servers:t,currentServer:n}=this.props;n||this.setServer(null===(e=t.first())||void 0===e?void 0:e.get("url"))}UNSAFE_componentWillReceiveProps(e){let{servers:t,setServerVariableValue:n,getServerVariable:r}=e;if(this.props.currentServer!==e.currentServer||this.props.servers!==e.servers){var o;let a=i()(t).call(t,(t=>t.get("url")===e.currentServer)),s=i()(o=this.props.servers).call(o,(e=>e.get("url")===this.props.currentServer))||(0,c.OrderedMap)();if(!a)return this.setServer(t.first().get("url"));let u=s.get("variables")||(0,c.OrderedMap)(),p=(i()(u).call(u,(e=>e.get("default")))||(0,c.OrderedMap)()).get("default"),f=a.get("variables")||(0,c.OrderedMap)(),h=(i()(f).call(f,(e=>e.get("default")))||(0,c.OrderedMap)()).get("default");l()(f).call(f,((t,o)=>{r(e.currentServer,o)&&p===h||n({server:e.currentServer,key:o,val:t.get("default")||""})}))}}render(){var e,t;let{servers:n,currentServer:r,getServerVariable:o,getEffectiveServerValue:a}=this.props,s=(i()(n).call(n,(e=>e.get("url")===r))||(0,c.OrderedMap)()).get("variables")||(0,c.OrderedMap)(),p=0!==s.size;return u.createElement("div",{className:"servers"},u.createElement("label",{htmlFor:"servers"},u.createElement("select",{onChange:this.onServerChange,value:r},l()(e=n.valueSeq()).call(e,(e=>u.createElement("option",{value:e.get("url"),key:e.get("url")},e.get("url"),e.get("description")&&` - ${e.get("description")}`))).toArray())),p?u.createElement("div",null,u.createElement("div",{className:"computed-url"},"Computed URL:",u.createElement("code",null,a(r))),u.createElement("h4",null,"Server variables"),u.createElement("table",null,u.createElement("tbody",null,l()(t=s.entrySeq()).call(t,(e=>{var t;let[n,a]=e;return u.createElement("tr",{key:n},u.createElement("td",null,n),u.createElement("td",null,a.get("enum")?u.createElement("select",{"data-variable":n,onChange:this.onServerVariableValueChange},l()(t=a.get("enum")).call(t,(e=>u.createElement("option",{selected:e===o(r,n),key:e,value:e},e)))):u.createElement("input",{type:"text",value:o(r,n)||"",onChange:this.onServerVariableValueChange,"data-variable":n})))}))))):null)}}},7779:(e,t,n)=>{"use strict";n.r(t),n.d(t,{OAS3ComponentWrapFactory:()=>c,isOAS3:()=>l,isSwagger2:()=>u});var r=n(23101),o=n.n(r),a=n(27043),i=n.n(a),s=n(67294);function l(e){const t=e.get("openapi");return"string"==typeof t&&(i()(t).call(t,"3.0.")&&t.length>4)}function u(e){const t=e.get("swagger");return"string"==typeof t&&i()(t).call(t,"2.0")}function c(e){return(t,n)=>r=>{if(n&&n.specSelectors&&n.specSelectors.specJson){return l(n.specSelectors.specJson())?s.createElement(e,o()({},r,n,{Ori:t})):s.createElement(t,r)}return console.warn("OAS3 wrapper: couldn't get spec"),null}}},97451:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(92044),o=n(73723),a=n(91741),i=n(76467),s=n(37761),l=n(67002),u=n(5065),c=n(62109);function p(){return{components:i.default,wrapComponents:s.default,statePlugins:{spec:{wrapSelectors:r,selectors:a},auth:{wrapSelectors:o},oas3:{actions:l,reducers:c.default,selectors:u}}}}},62109:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(8712),o=n.n(r),a=n(86),i=n.n(a),s=n(24282),l=n.n(s),u=n(43393),c=n(67002);const p={[c.UPDATE_SELECTED_SERVER]:(e,t)=>{let{payload:{selectedServerUrl:n,namespace:r}}=t;const o=r?[r,"selectedServer"]:["selectedServer"];return e.setIn(o,n)},[c.UPDATE_REQUEST_BODY_VALUE]:(e,t)=>{let{payload:{value:n,pathMethod:r}}=t,[a,s]=r;if(!u.Map.isMap(n))return e.setIn(["requestData",a,s,"bodyValue"],n);let l,c=e.getIn(["requestData",a,s,"bodyValue"])||(0,u.Map)();u.Map.isMap(c)||(c=(0,u.Map)());const[...p]=o()(n).call(n);return i()(p).call(p,(e=>{let t=n.getIn([e]);c.has(e)&&u.Map.isMap(t)||(l=c.setIn([e,"value"],t))})),e.setIn(["requestData",a,s,"bodyValue"],l)},[c.UPDATE_REQUEST_BODY_VALUE_RETAIN_FLAG]:(e,t)=>{let{payload:{value:n,pathMethod:r}}=t,[o,a]=r;return e.setIn(["requestData",o,a,"retainBodyValue"],n)},[c.UPDATE_REQUEST_BODY_INCLUSION]:(e,t)=>{let{payload:{value:n,pathMethod:r,name:o}}=t,[a,i]=r;return e.setIn(["requestData",a,i,"bodyInclusion",o],n)},[c.UPDATE_ACTIVE_EXAMPLES_MEMBER]:(e,t)=>{let{payload:{name:n,pathMethod:r,contextType:o,contextName:a}}=t,[i,s]=r;return e.setIn(["examples",i,s,o,a,"activeExample"],n)},[c.UPDATE_REQUEST_CONTENT_TYPE]:(e,t)=>{let{payload:{value:n,pathMethod:r}}=t,[o,a]=r;return e.setIn(["requestData",o,a,"requestContentType"],n)},[c.UPDATE_RESPONSE_CONTENT_TYPE]:(e,t)=>{let{payload:{value:n,path:r,method:o}}=t;return e.setIn(["requestData",r,o,"responseContentType"],n)},[c.UPDATE_SERVER_VARIABLE_VALUE]:(e,t)=>{let{payload:{server:n,namespace:r,key:o,val:a}}=t;const i=r?[r,"serverVariableValues",n,o]:["serverVariableValues",n,o];return e.setIn(i,a)},[c.SET_REQUEST_BODY_VALIDATE_ERROR]:(e,t)=>{let{payload:{path:n,method:r,validationErrors:o}}=t,a=[];if(a.push("Required field is not provided"),o.missingBodyValue)return e.setIn(["requestData",n,r,"errors"],(0,u.fromJS)(a));if(o.missingRequiredKeys&&o.missingRequiredKeys.length>0){const{missingRequiredKeys:t}=o;return e.updateIn(["requestData",n,r,"bodyValue"],(0,u.fromJS)({}),(e=>l()(t).call(t,((e,t)=>e.setIn([t,"errors"],(0,u.fromJS)(a))),e)))}return console.warn("unexpected result: SET_REQUEST_BODY_VALIDATE_ERROR"),e},[c.CLEAR_REQUEST_BODY_VALIDATE_ERROR]:(e,t)=>{let{payload:{path:n,method:r}}=t;const a=e.getIn(["requestData",n,r,"bodyValue"]);if(!u.Map.isMap(a))return e.setIn(["requestData",n,r,"errors"],(0,u.fromJS)([]));const[...i]=o()(a).call(a);return i?e.updateIn(["requestData",n,r,"bodyValue"],(0,u.fromJS)({}),(e=>l()(i).call(i,((e,t)=>e.setIn([t,"errors"],(0,u.fromJS)([]))),e))):e},[c.CLEAR_REQUEST_BODY_VALUE]:(e,t)=>{let{payload:{pathMethod:n}}=t,[r,o]=n;const a=e.getIn(["requestData",r,o,"bodyValue"]);return a?u.Map.isMap(a)?e.setIn(["requestData",r,o,"bodyValue"],(0,u.Map)()):e.setIn(["requestData",r,o,"bodyValue"],""):e}}},5065:(e,t,n)=>{"use strict";n.r(t),n.d(t,{activeExamplesMember:()=>_,hasUserEditedBody:()=>w,requestBodyErrors:()=>x,requestBodyInclusionSetting:()=>E,requestBodyValue:()=>y,requestContentType:()=>S,responseContentType:()=>A,selectDefaultRequestBodyValue:()=>b,selectedServer:()=>g,serverEffectiveValue:()=>O,serverVariableValue:()=>C,serverVariables:()=>k,shouldRetainRequestBodyValue:()=>v,validateBeforeExecute:()=>j,validateShallowRequired:()=>I});var r=n(97606),o=n.n(r),a=n(86),i=n.n(a),s=n(28222),l=n.n(s),u=n(11882),c=n.n(u),p=n(43393),f=n(7779),h=n(42458),d=n(90242);function m(e){return function(){for(var t=arguments.length,n=new Array(t),r=0;r{const r=t.getSystem().specSelectors.specJson();return(0,f.isOAS3)(r)?e(...n):null}}}const g=m(((e,t)=>{const n=t?[t,"selectedServer"]:["selectedServer"];return e.getIn(n)||""})),y=m(((e,t,n)=>e.getIn(["requestData",t,n,"bodyValue"])||null)),v=m(((e,t,n)=>e.getIn(["requestData",t,n,"retainBodyValue"])||!1)),b=(e,t,n)=>e=>{const{oas3Selectors:r,specSelectors:o}=e.getSystem(),a=o.specJson();if((0,f.isOAS3)(a)){const e=r.requestContentType(t,n);if(e)return(0,h.getDefaultRequestBodyValue)(o.specResolvedSubtree(["paths",t,n,"requestBody"]),e,r.activeExamplesMember(t,n,"requestBody","requestBody"))}return null},w=(e,t,n)=>e=>{const{oas3Selectors:r,specSelectors:o}=e.getSystem(),a=o.specJson();if((0,f.isOAS3)(a)){let e=!1;const a=r.requestContentType(t,n);let i=r.requestBodyValue(t,n);if(p.Map.isMap(i)&&(i=(0,d.Pz)(i.mapEntries((e=>p.Map.isMap(e[1])?[e[0],e[1].get("value")]:e)).toJS())),p.List.isList(i)&&(i=(0,d.Pz)(i)),a){const s=(0,h.getDefaultRequestBodyValue)(o.specResolvedSubtree(["paths",t,n,"requestBody"]),a,r.activeExamplesMember(t,n,"requestBody","requestBody"));e=!!i&&i!==s}return e}return null},E=m(((e,t,n)=>e.getIn(["requestData",t,n,"bodyInclusion"])||(0,p.Map)())),x=m(((e,t,n)=>e.getIn(["requestData",t,n,"errors"])||null)),_=m(((e,t,n,r,o)=>e.getIn(["examples",t,n,r,o,"activeExample"])||null)),S=m(((e,t,n)=>e.getIn(["requestData",t,n,"requestContentType"])||null)),A=m(((e,t,n)=>e.getIn(["requestData",t,n,"responseContentType"])||null)),C=m(((e,t,n)=>{let r;if("string"!=typeof t){const{server:e,namespace:o}=t;r=o?[o,"serverVariableValues",e,n]:["serverVariableValues",e,n]}else{r=["serverVariableValues",t,n]}return e.getIn(r)||null})),k=m(((e,t)=>{let n;if("string"!=typeof t){const{server:e,namespace:r}=t;n=r?[r,"serverVariableValues",e]:["serverVariableValues",e]}else{n=["serverVariableValues",t]}return e.getIn(n)||(0,p.OrderedMap)()})),O=m(((e,t)=>{var n,r;if("string"!=typeof t){const{server:o,namespace:a}=t;r=o,n=a?e.getIn([a,"serverVariableValues",r]):e.getIn(["serverVariableValues",r])}else r=t,n=e.getIn(["serverVariableValues",r]);n=n||(0,p.OrderedMap)();let a=r;return o()(n).call(n,((e,t)=>{a=a.replace(new RegExp(`{${t}}`,"g"),e)})),a})),j=(T=(e,t)=>((e,t)=>(t=t||[],!!e.getIn(["requestData",...t,"bodyValue"])))(e,t),function(){for(var e=arguments.length,t=new Array(e),n=0;n{const n=e.getSystem().specSelectors.specJson();let r=[...t][1]||[];return!n.getIn(["paths",...r,"requestBody","required"])||T(...t)}});var T;const I=(e,t)=>{var n;let{oas3RequiredRequestBodyContentType:r,oas3RequestContentType:o,oas3RequestBodyValue:a}=t,s=[];if(!p.Map.isMap(a))return s;let u=[];return i()(n=l()(r.requestContentType)).call(n,(e=>{if(e===o){let t=r.requestContentType[e];i()(t).call(t,(e=>{c()(u).call(u,e)<0&&u.push(e)}))}})),i()(u).call(u,(e=>{a.getIn([e,"value"])||s.push(e)})),s}},91741:(e,t,n)=>{"use strict";n.r(t),n.d(t,{isSwagger2:()=>p,servers:()=>u});var r=n(20573),o=n(43393),a=n(7779);const i=e=>e||(0,o.Map)(),s=(0,r.P1)(i,(e=>e.get("json",(0,o.Map)()))),l=(0,r.P1)(i,(e=>e.get("resolved",(0,o.Map)()))),u=(c=(0,r.P1)((e=>{let t=l(e);return t.count()<1&&(t=s(e)),t}),(e=>e.getIn(["servers"])||(0,o.Map)())),()=>function(e){const t=e.getSystem().specSelectors.specJson();if((0,a.isOAS3)(t)){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o()=>{const e=t.getSystem().specSelectors.specJson();return(0,a.isSwagger2)(e)}},92044:(e,t,n)=>{"use strict";n.r(t),n.d(t,{basePath:()=>y,consumes:()=>v,definitions:()=>h,hasHost:()=>d,host:()=>g,isOAS3:()=>x,isSwagger2:()=>_,produces:()=>b,schemes:()=>w,securityDefinitions:()=>m,servers:()=>E});var r=n(20573),o=n(33881),a=n(43393),i=n(7779);function s(e){return(t,n)=>function(){const r=n.getSystem().specSelectors.specJson();return(0,i.isOAS3)(r)?e(...arguments):t(...arguments)}}const l=e=>e||(0,a.Map)(),u=s((0,r.P1)((()=>null))),c=(0,r.P1)(l,(e=>e.get("json",(0,a.Map)()))),p=(0,r.P1)(l,(e=>e.get("resolved",(0,a.Map)()))),f=e=>{let t=p(e);return t.count()<1&&(t=c(e)),t},h=s((0,r.P1)(f,(e=>{const t=e.getIn(["components","schemas"]);return a.Map.isMap(t)?t:(0,a.Map)()}))),d=s((e=>f(e).hasIn(["servers",0]))),m=s((0,r.P1)(o.specJsonWithResolvedSubtrees,(e=>e.getIn(["components","securitySchemes"])||null))),g=u,y=u,v=u,b=u,w=u,E=s((0,r.P1)(f,(e=>e.getIn(["servers"])||(0,a.Map)()))),x=(e,t)=>()=>{const e=t.getSystem().specSelectors.specJson();return(0,i.isOAS3)(a.Map.isMap(e)?e:(0,a.Map)())},_=(e,t)=>()=>{const e=t.getSystem().specSelectors.specJson();return(0,i.isSwagger2)(a.Map.isMap(e)?e:(0,a.Map)())}},70356:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(7779).OAS3ComponentWrapFactory)((e=>{let{Ori:t,...n}=e;const{schema:o,getComponent:a,errSelectors:i,authorized:s,onAuthChange:l,name:u}=n,c=a("HttpAuth");return"http"===o.get("type")?r.createElement(c,{key:u,schema:o,name:u,errSelectors:i,authorized:s,getComponent:a,onChange:l}):r.createElement(t,n)}))},37761:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(22460),o=n(70356),a=n(69487),i=n(50058),s=n(53499),l=n(90287);const u={Markdown:r.default,AuthItem:o.default,JsonSchema_string:l.default,VersionStamp:a.default,model:s.default,onlineValidatorBadge:i.default}},90287:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(7779).OAS3ComponentWrapFactory)((e=>{let{Ori:t,...n}=e;const{schema:o,getComponent:a,errors:i,onChange:s}=n,l=o&&o.get?o.get("format"):null,u=o&&o.get?o.get("type"):null,c=a("Input");return u&&"string"===u&&l&&("binary"===l||"base64"===l)?r.createElement(c,{type:"file",className:i.length?"invalid":"",title:i.length?i:"",onChange:e=>{s(e.target.files[0])},disabled:t.isDisabled}):r.createElement(t,n)}))},22460:(e,t,n)=>{"use strict";n.r(t),n.d(t,{Markdown:()=>f,default:()=>h});var r=n(81607),o=n.n(r),a=n(67294),i=n(94184),s=n.n(i),l=n(89927),u=n(7779),c=n(94994);const p=new l._("commonmark");p.block.ruler.enable(["table"]),p.set({linkTarget:"_blank"});const f=e=>{let{source:t,className:n="",getConfigs:r}=e;if("string"!=typeof t)return null;if(t){const{useUnsafeMarkdown:e}=r(),i=p.render(t),l=(0,c.s)(i,{useUnsafeMarkdown:e});let u;return"string"==typeof l&&(u=o()(l).call(l)),a.createElement("div",{dangerouslySetInnerHTML:{__html:u},className:s()(n,"renderedMarkdown")})}return null};f.defaultProps={getConfigs:()=>({useUnsafeMarkdown:!1})};const h=(0,u.OAS3ComponentWrapFactory)(f)},53499:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(23101),o=n.n(r),a=n(67294),i=n(7779),s=n(53795);class l extends a.Component{render(){let{getConfigs:e,schema:t}=this.props,n=["model-box"],r=null;return!0===t.get("deprecated")&&(n.push("deprecated"),r=a.createElement("span",{className:"model-deprecated-warning"},"Deprecated:")),a.createElement("div",{className:n.join(" ")},r,a.createElement(s.Z,o()({},this.props,{getConfigs:e,depth:1,expandDepth:this.props.expandDepth||0})))}}const u=(0,i.OAS3ComponentWrapFactory)(l)},50058:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(7779),o=n(5623);const a=(0,r.OAS3ComponentWrapFactory)(o.Z)},69487:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(7779).OAS3ComponentWrapFactory)((e=>{const{Ori:t}=e;return r.createElement("span",null,r.createElement(t,e),r.createElement("small",{className:"version-stamp"},r.createElement("pre",{className:"version"},"OAS3")))}))},28560:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(87198),o=n.n(r);let a=!1;function i(){return{statePlugins:{spec:{wrapActions:{updateSpec:e=>function(){return a=!0,e(...arguments)},updateJsonSpec:(e,t)=>function(){const n=t.getConfigs().onComplete;return a&&"function"==typeof n&&(o()(n,0),a=!1),e(...arguments)}}}}}}},92135:(e,t,n)=>{"use strict";n.r(t),n.d(t,{requestSnippetGenerator_curl_bash:()=>A,requestSnippetGenerator_curl_cmd:()=>C,requestSnippetGenerator_curl_powershell:()=>S});var r=n(11882),o=n.n(r),a=n(81607),i=n.n(a),s=n(35627),l=n.n(s),u=n(97606),c=n.n(u),p=n(12196),f=n.n(p),h=n(74386),d=n.n(h),m=n(58118),g=n.n(m),y=n(27504),v=n(43393);const b=e=>{var t;const n="_**[]";return o()(e).call(e,n)<0?e:i()(t=e.split(n)[0]).call(t)},w=e=>"-d "===e||/^[_\/-]/g.test(e)?e:"'"+e.replace(/'/g,"'\\''")+"'",E=e=>"-d "===(e=e.replace(/\^/g,"^^").replace(/\\"/g,'\\\\"').replace(/"/g,'""').replace(/\n/g,"^\n"))?e.replace(/-d /g,"-d ^\n"):/^[_\/-]/g.test(e)?e:'"'+e+'"',x=e=>"-d "===e?e:/\n/.test(e)?'@"\n'+e.replace(/"/g,'\\"').replace(/`/g,"``").replace(/\$/,"`$")+'\n"@':/^[_\/-]/g.test(e)?e:"'"+e.replace(/"/g,'""').replace(/'/g,"''")+"'";const _=function(e,t,n){let r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"",o=!1,a="";const i=function(){for(var e=arguments.length,n=new Array(e),r=0;ra+=` ${n}`,p=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;return a+=f()(" ").call(" ",e)};let h=e.get("headers");if(a+="curl"+r,e.has("curlOptions")&&i(...e.get("curlOptions")),i("-X",e.get("method")),u(),p(),s(`${e.get("url")}`),h&&h.size)for(let t of d()(m=e.get("headers")).call(m)){var m;u(),p();let[e,n]=t;s("-H",`${e}: ${n}`),o=o||/^content-type$/i.test(e)&&/^multipart\/form-data$/i.test(n)}const w=e.get("body");var E;if(w)if(o&&g()(E=["POST","PUT","PATCH"]).call(E,e.get("method")))for(let[e,t]of w.entrySeq()){let n=b(e);u(),p(),s("-F"),t instanceof y.Z.File?i(`${n}=@${t.name}${t.type?`;type=${t.type}`:""}`):i(`${n}=${t}`)}else if(w instanceof y.Z.File)u(),p(),s(`--data-binary '@${w.name}'`);else{u(),p(),s("-d ");let t=w;v.Map.isMap(t)?s(function(e){let t=[];for(let[n,r]of e.get("body").entrySeq()){let e=b(n);r instanceof y.Z.File?t.push(` "${e}": {\n "name": "${r.name}"${r.type?`,\n "type": "${r.type}"`:""}\n }`):t.push(` "${e}": ${l()(r,null,2).replace(/(\r\n|\r|\n)/g,"\n ")}`)}return`{\n${t.join(",\n")}\n}`}(e)):("string"!=typeof t&&(t=l()(t)),s(t))}else w||"POST"!==e.get("method")||(u(),p(),s("-d ''"));return a},S=e=>_(e,x,"`\n",".exe"),A=e=>_(e,w,"\\\n"),C=e=>_(e,E,"^\n")},86575:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(92135),o=n(4669),a=n(84206);const i=()=>({components:{RequestSnippets:a.default},fn:r,statePlugins:{requestSnippets:{selectors:o}}})},84206:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>w});var r=n(14418),o=n.n(r),a=n(25110),i=n.n(a),s=n(86),l=n.n(s),u=n(97606),c=n.n(u),p=n(67294),f=n(27361),h=n.n(f),d=n(23560),m=n.n(d),g=n(74855),y=n(33424);const v={cursor:"pointer",lineHeight:1,display:"inline-flex",backgroundColor:"rgb(250, 250, 250)",paddingBottom:"0",paddingTop:"0",border:"1px solid rgb(51, 51, 51)",borderRadius:"4px 4px 0 0",boxShadow:"none",borderBottom:"none"},b={cursor:"pointer",lineHeight:1,display:"inline-flex",backgroundColor:"rgb(51, 51, 51)",boxShadow:"none",border:"1px solid rgb(51, 51, 51)",paddingBottom:"0",paddingTop:"0",borderRadius:"4px 4px 0 0",marginTop:"-5px",marginRight:"-5px",marginLeft:"-5px",zIndex:"9999",borderBottom:"none"},w=e=>{var t,n;let{request:r,requestSnippetsSelectors:a,getConfigs:s}=e;const u=m()(s)?s():null,f=!1!==h()(u,"syntaxHighlight")&&h()(u,"syntaxHighlight.activated",!0),d=(0,p.useRef)(null),[w,E]=(0,p.useState)(null===(t=a.getSnippetGenerators())||void 0===t?void 0:t.keySeq().first()),[x,_]=(0,p.useState)(null==a?void 0:a.getDefaultExpanded());(0,p.useEffect)((()=>{}),[]),(0,p.useEffect)((()=>{var e;const t=o()(e=i()(d.current.childNodes)).call(e,(e=>{var t;return!!e.nodeType&&(null===(t=e.classList)||void 0===t?void 0:t.contains("curl-command"))}));return l()(t).call(t,(e=>e.addEventListener("mousewheel",j,{passive:!1}))),()=>{l()(t).call(t,(e=>e.removeEventListener("mousewheel",j)))}}),[r]);const S=a.getSnippetGenerators(),A=S.get(w),C=A.get("fn")(r),k=()=>{_(!x)},O=e=>e===w?b:v,j=e=>{const{target:t,deltaY:n}=e,{scrollHeight:r,offsetHeight:o,scrollTop:a}=t;r>o&&(0===a&&n<0||o+a>=r&&n>0)&&e.preventDefault()},T=f?p.createElement(y.d3,{language:A.get("syntax"),className:"curl microlight",style:(0,y.C2)(h()(u,"syntaxHighlight.theme"))},C):p.createElement("textarea",{readOnly:!0,className:"curl",value:C});return p.createElement("div",{className:"request-snippets",ref:d},p.createElement("div",{style:{width:"100%",display:"flex",justifyContent:"flex-start",alignItems:"center",marginBottom:"15px"}},p.createElement("h4",{onClick:()=>k(),style:{cursor:"pointer"}},"Snippets"),p.createElement("button",{onClick:()=>k(),style:{border:"none",background:"none"},title:x?"Collapse operation":"Expand operation"},p.createElement("svg",{className:"arrow",width:"10",height:"10"},p.createElement("use",{href:x?"#large-arrow-down":"#large-arrow",xlinkHref:x?"#large-arrow-down":"#large-arrow"})))),x&&p.createElement("div",{className:"curl-command"},p.createElement("div",{style:{paddingLeft:"15px",paddingRight:"10px",width:"100%",display:"flex"}},c()(n=S.entrySeq()).call(n,(e=>{let[t,n]=e;return p.createElement("div",{style:O(t),className:"btn",key:t,onClick:()=>(e=>{w!==e&&E(e)})(t)},p.createElement("h4",{style:t===w?{color:"white"}:{}},n.get("title")))}))),p.createElement("div",{className:"copy-to-clipboard"},p.createElement(g.CopyToClipboard,{text:C},p.createElement("button",null))),p.createElement("div",null,T)))}},4669:(e,t,n)=>{"use strict";n.r(t),n.d(t,{getActiveLanguage:()=>d,getDefaultExpanded:()=>m,getGenerators:()=>f,getSnippetGenerators:()=>h});var r=n(14418),o=n.n(r),a=n(58118),i=n.n(a),s=n(97606),l=n.n(s),u=n(20573),c=n(43393);const p=e=>e||(0,c.Map)(),f=(0,u.P1)(p,(e=>{const t=e.get("languages"),n=e.get("generators",(0,c.Map)());return!t||t.isEmpty()?n:o()(n).call(n,((e,n)=>i()(t).call(t,n)))})),h=e=>t=>{var n,r;let{fn:a}=t;return o()(n=l()(r=f(e)).call(r,((e,t)=>{const n=(e=>a[`requestSnippetGenerator_${e}`])(t);return"function"!=typeof n?null:e.set("fn",n)}))).call(n,(e=>e))},d=(0,u.P1)(p,(e=>e.get("activeLanguage"))),m=(0,u.P1)(p,(e=>e.get("defaultExpanded")))},36195:(e,t,n)=>{"use strict";n.r(t),n.d(t,{ErrorBoundary:()=>i,default:()=>s});var r=n(67294),o=n(56189),a=n(29403);class i extends r.Component{static getDerivedStateFromError(e){return{hasError:!0,error:e}}constructor(){super(...arguments),this.state={hasError:!1,error:null}}componentDidCatch(e,t){this.props.fn.componentDidCatch(e,t)}render(){const{getComponent:e,targetName:t,children:n}=this.props;if(this.state.hasError){const n=e("Fallback");return r.createElement(n,{name:t})}return n}}i.defaultProps={targetName:"this component",getComponent:()=>a.default,fn:{componentDidCatch:o.componentDidCatch},children:null};const s=i},29403:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=e=>{let{name:t}=e;return r.createElement("div",{className:"fallback"},"😱 ",r.createElement("i",null,"Could not render ","t"===t?"this component":t,", see the console."))}},56189:(e,t,n)=>{"use strict";n.r(t),n.d(t,{componentDidCatch:()=>i,withErrorBoundary:()=>s});var r=n(23101),o=n.n(r),a=n(67294);const i=console.error,s=e=>t=>{const{getComponent:n,fn:r}=e(),i=n("ErrorBoundary"),s=r.getDisplayName(t);class l extends a.Component{render(){return a.createElement(i,{targetName:s,getComponent:n,fn:r},a.createElement(t,o()({},this.props,this.context)))}}var u;return l.displayName=`WithErrorBoundary(${s})`,(u=t).prototype&&u.prototype.isReactComponent&&(l.prototype.mapStateToProps=t.prototype.mapStateToProps),l}},27621:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(47475),o=n.n(r),a=n(7287),i=n.n(a),s=n(36195),l=n(29403),u=n(56189);const c=function(){let{componentList:e=[],fullOverride:t=!1}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return n=>{var r;let{getSystem:a}=n;const c=t?e:["App","BaseLayout","VersionPragmaFilter","InfoContainer","ServersContainer","SchemesContainer","AuthorizeBtnContainer","FilterContainer","Operations","OperationContainer","parameters","responses","OperationServers","Models","ModelWrapper",...e],p=i()(c,o()(r=Array(c.length)).call(r,((e,t)=>{let{fn:n}=t;return n.withErrorBoundary(e)})));return{fn:{componentDidCatch:u.componentDidCatch,withErrorBoundary:(0,u.withErrorBoundary)(a)},components:{ErrorBoundary:s.default,Fallback:l.default},wrapComponents:p}}}},57050:(e,t,n)=>{"use strict";n.r(t),n.d(t,{createXMLExample:()=>z,inferSchema:()=>U,memoizedCreateXMLExample:()=>V,memoizedSampleFromSchema:()=>H,sampleFromSchema:()=>q,sampleFromSchemaGeneric:()=>F});var r=n(11882),o=n.n(r),a=n(86),i=n.n(a),s=n(58309),l=n.n(s),u=n(58118),c=n.n(u),p=n(92039),f=n.n(p),h=n(24278),d=n.n(h),m=n(51679),g=n.n(m),y=n(39022),v=n.n(y),b=n(97606),w=n.n(b),E=n(35627),x=n.n(E),_=n(53479),S=n.n(_),A=n(14419),C=n.n(A),k=n(41609),O=n.n(k),j=n(90242),T=n(60314);const I={string:e=>e.pattern?(e=>{try{return new(C())(e).gen()}catch(e){return"string"}})(e.pattern):"string",string_email:()=>"user@example.com","string_date-time":()=>(new Date).toISOString(),string_date:()=>(new Date).toISOString().substring(0,10),string_uuid:()=>"3fa85f64-5717-4562-b3fc-2c963f66afa6",string_hostname:()=>"example.com",string_ipv4:()=>"198.51.100.42",string_ipv6:()=>"2001:0db8:5b96:0000:0000:426f:8e17:642a",number:()=>0,number_float:()=>0,integer:()=>0,boolean:e=>"boolean"!=typeof e.default||e.default},N=e=>{e=(0,j.mz)(e);let{type:t,format:n}=e,r=I[`${t}_${n}`]||I[t];return(0,j.Wl)(r)?r(e):"Unknown Type: "+e.type},P=e=>(0,j.XV)(e,"$$ref",(e=>"string"==typeof e&&o()(e).call(e,"#")>-1)),R=["maxProperties","minProperties"],M=["minItems","maxItems"],D=["minimum","maximum","exclusiveMinimum","exclusiveMaximum"],L=["minLength","maxLength"],B=function(e,t){var n;let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};var a;(i()(n=["example","default","enum","xml","type",...R,...M,...D,...L]).call(n,(n=>(n=>{void 0===t[n]&&void 0!==e[n]&&(t[n]=e[n])})(n))),void 0!==e.required&&l()(e.required))&&(void 0!==t.required&&t.required.length||(t.required=[]),i()(a=e.required).call(a,(e=>{var n;c()(n=t.required).call(n,e)||t.required.push(e)})));if(e.properties){t.properties||(t.properties={});let n=(0,j.mz)(e.properties);for(let a in n){var s;if(Object.prototype.hasOwnProperty.call(n,a))if(!n[a]||!n[a].deprecated)if(!n[a]||!n[a].readOnly||r.includeReadOnly)if(!n[a]||!n[a].writeOnly||r.includeWriteOnly)if(!t.properties[a])t.properties[a]=n[a],!e.required&&l()(e.required)&&-1!==o()(s=e.required).call(s,a)&&(t.required?t.required.push(a):t.required=[a])}}return e.items&&(t.items||(t.items={}),t.items=B(e.items,t.items,r)),t},F=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,r=arguments.length>3&&void 0!==arguments[3]&&arguments[3];e&&(0,j.Wl)(e.toJS)&&(e=e.toJS());let a=void 0!==n||e&&void 0!==e.example||e&&void 0!==e.default;const s=!a&&e&&e.oneOf&&e.oneOf.length>0,u=!a&&e&&e.anyOf&&e.anyOf.length>0;if(!a&&(s||u)){const n=(0,j.mz)(s?e.oneOf[0]:e.anyOf[0]);if(B(n,e,t),!e.xml&&n.xml&&(e.xml=n.xml),void 0!==e.example&&void 0!==n.example)a=!0;else if(n.properties){e.properties||(e.properties={});let r=(0,j.mz)(n.properties);for(let a in r){var p;if(Object.prototype.hasOwnProperty.call(r,a))if(!r[a]||!r[a].deprecated)if(!r[a]||!r[a].readOnly||t.includeReadOnly)if(!r[a]||!r[a].writeOnly||t.includeWriteOnly)if(!e.properties[a])e.properties[a]=r[a],!n.required&&l()(n.required)&&-1!==o()(p=n.required).call(p,a)&&(e.required?e.required.push(a):e.required=[a])}}}const h={};let{xml:m,type:y,example:b,properties:E,additionalProperties:x,items:_}=e||{},{includeReadOnly:S,includeWriteOnly:A}=t;m=m||{};let C,{name:k,prefix:T,namespace:I}=m,L={};if(r&&(k=k||"notagname",C=(T?T+":":"")+k,I)){h[T?"xmlns:"+T:"xmlns"]=I}r&&(L[C]=[]);const U=t=>f()(t).call(t,(t=>Object.prototype.hasOwnProperty.call(e,t)));e&&!y&&(E||x||U(R)?y="object":_||U(M)?y="array":U(D)?(y="number",e.type="number"):a||e.enum||(y="string",e.type="string"));const z=t=>{var n,r,o,a,i;null!==(null===(n=e)||void 0===n?void 0:n.maxItems)&&void 0!==(null===(r=e)||void 0===r?void 0:r.maxItems)&&(t=d()(t).call(t,0,null===(i=e)||void 0===i?void 0:i.maxItems));if(null!==(null===(o=e)||void 0===o?void 0:o.minItems)&&void 0!==(null===(a=e)||void 0===a?void 0:a.minItems)){let n=0;for(;t.length<(null===(s=e)||void 0===s?void 0:s.minItems);){var s;t.push(t[n++%t.length])}}return t},q=(0,j.mz)(E);let $,V=0;const H=()=>e&&null!==e.maxProperties&&void 0!==e.maxProperties&&V>=e.maxProperties,W=t=>!e||null===e.maxProperties||void 0===e.maxProperties||!H()&&(!(t=>{var n;return!(e&&e.required&&e.required.length&&c()(n=e.required).call(n,t))})(t)||e.maxProperties-V-(()=>{if(!e||!e.required)return 0;let t=0;var n,o;return r?i()(n=e.required).call(n,(e=>t+=void 0===L[e]?0:1)):i()(o=e.required).call(o,(e=>{var n;return t+=void 0===(null===(n=L[C])||void 0===n?void 0:g()(n).call(n,(t=>void 0!==t[e])))?0:1})),e.required.length-t})()>0);if($=r?function(n){let o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;if(e&&q[n]){if(q[n].xml=q[n].xml||{},q[n].xml.attribute){const e=l()(q[n].enum)?q[n].enum[0]:void 0,t=q[n].example,r=q[n].default;return void(h[q[n].xml.name||n]=void 0!==t?t:void 0!==r?r:void 0!==e?e:N(q[n]))}q[n].xml.name=q[n].xml.name||n}else q[n]||!1===x||(q[n]={xml:{name:n}});let a=F(e&&q[n]||void 0,t,o,r);var i;W(n)&&(V++,l()(a)?L[C]=v()(i=L[C]).call(i,a):L[C].push(a))}:(n,o)=>{if(W(n)){if(Object.prototype.hasOwnProperty.call(e,"discriminator")&&e.discriminator&&Object.prototype.hasOwnProperty.call(e.discriminator,"mapping")&&e.discriminator.mapping&&Object.prototype.hasOwnProperty.call(e,"$$ref")&&e.$$ref&&e.discriminator.propertyName===n){for(let t in e.discriminator.mapping)if(-1!==e.$$ref.search(e.discriminator.mapping[t])){L[n]=t;break}}else L[n]=F(q[n],t,o,r);V++}},a){let o;if(o=P(void 0!==n?n:void 0!==b?b:e.default),!r){if("number"==typeof o&&"string"===y)return`${o}`;if("string"!=typeof o||"string"===y)return o;try{return JSON.parse(o)}catch(e){return o}}if(e||(y=l()(o)?"array":typeof o),"array"===y){if(!l()(o)){if("string"==typeof o)return o;o=[o]}const n=e?e.items:void 0;n&&(n.xml=n.xml||m||{},n.xml.name=n.xml.name||m.name);let a=w()(o).call(o,(e=>F(n,t,e,r)));return a=z(a),m.wrapped?(L[C]=a,O()(h)||L[C].push({_attr:h})):L=a,L}if("object"===y){if("string"==typeof o)return o;for(let t in o)Object.prototype.hasOwnProperty.call(o,t)&&(e&&q[t]&&q[t].readOnly&&!S||e&&q[t]&&q[t].writeOnly&&!A||(e&&q[t]&&q[t].xml&&q[t].xml.attribute?h[q[t].xml.name||t]=o[t]:$(t,o[t])));return O()(h)||L[C].push({_attr:h}),L}return L[C]=O()(h)?o:[{_attr:h},o],L}if("object"===y){for(let e in q)Object.prototype.hasOwnProperty.call(q,e)&&(q[e]&&q[e].deprecated||q[e]&&q[e].readOnly&&!S||q[e]&&q[e].writeOnly&&!A||$(e));if(r&&h&&L[C].push({_attr:h}),H())return L;if(!0===x)r?L[C].push({additionalProp:"Anything can be here"}):L.additionalProp1={},V++;else if(x){const n=(0,j.mz)(x),o=F(n,t,void 0,r);if(r&&n.xml&&n.xml.name&&"notagname"!==n.xml.name)L[C].push(o);else{const t=null!==e.minProperties&&void 0!==e.minProperties&&VF(B(_,e,t),t,void 0,r)));else if(l()(_.oneOf)){var G;n=w()(G=_.oneOf).call(G,(e=>F(B(_,e,t),t,void 0,r)))}else{if(!(!r||r&&m.wrapped))return F(_,t,void 0,r);n=[F(_,t,void 0,r)]}return n=z(n),r&&m.wrapped?(L[C]=n,O()(h)||L[C].push({_attr:h}),L):n}let Z;if(e&&l()(e.enum))Z=(0,j.AF)(e.enum)[0];else{if(!e)return;if(Z=N(e),"number"==typeof Z){let t=e.minimum;null!=t&&(e.exclusiveMinimum&&t++,Z=t);let n=e.maximum;null!=n&&(e.exclusiveMaximum&&n--,Z=n)}if("string"==typeof Z&&(null!==e.maxLength&&void 0!==e.maxLength&&(Z=d()(Z).call(Z,0,e.maxLength)),null!==e.minLength&&void 0!==e.minLength)){let t=0;for(;Z.length(e.schema&&(e=e.schema),e.properties&&(e.type="object"),e),z=(e,t,n)=>{const r=F(e,t,n,!0);if(r)return"string"==typeof r?r:S()(r,{declaration:!0,indent:"\t"})},q=(e,t,n)=>F(e,t,n,!1),$=(e,t,n)=>[e,x()(t),x()(n)],V=(0,T.Z)(z,$),H=(0,T.Z)(q,$)},8883:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(57050);function o(){return{fn:r}}},51228:(e,t,n)=>{"use strict";n.r(t),n.d(t,{CLEAR_REQUEST:()=>Q,CLEAR_RESPONSE:()=>Y,CLEAR_VALIDATE_PARAMS:()=>X,LOG_REQUEST:()=>Z,SET_MUTATED_REQUEST:()=>G,SET_REQUEST:()=>K,SET_RESPONSE:()=>J,SET_SCHEME:()=>re,UPDATE_EMPTY_PARAM_INCLUSION:()=>H,UPDATE_JSON:()=>$,UPDATE_OPERATION_META_VALUE:()=>ee,UPDATE_PARAM:()=>V,UPDATE_RESOLVED:()=>te,UPDATE_RESOLVED_SUBTREE:()=>ne,UPDATE_SPEC:()=>z,UPDATE_URL:()=>q,VALIDATE_PARAMS:()=>W,changeConsumesValue:()=>xe,changeParam:()=>me,changeParamByIdentity:()=>ge,changeProducesValue:()=>_e,clearRequest:()=>Ie,clearResponse:()=>Te,clearValidateParams:()=>Ee,execute:()=>je,executeRequest:()=>Oe,invalidateResolvedSubtreeCache:()=>ve,logRequest:()=>ke,parseToJson:()=>ue,requestResolvedSubtree:()=>de,resolveSpec:()=>pe,setMutatedRequest:()=>Ce,setRequest:()=>Ae,setResponse:()=>Se,setScheme:()=>Ne,updateEmptyParamInclusion:()=>we,updateJsonSpec:()=>le,updateResolved:()=>ie,updateResolvedSubtree:()=>ye,updateSpec:()=>ae,updateUrl:()=>se,validateParams:()=>be});var r=n(58309),o=n.n(r),a=n(97606),i=n.n(a),s=n(96718),l=n.n(s),u=n(24282),c=n.n(u),p=n(2250),f=n.n(p),h=n(6226),d=n.n(h),m=n(14418),g=n.n(m),y=n(3665),v=n.n(y),b=n(11882),w=n.n(b),E=n(86),x=n.n(E),_=n(28222),S=n.n(_),A=n(76986),C=n.n(A),k=n(70586),O=n.n(k),j=n(1272),T=n(43393),I=n(84564),N=n.n(I),P=n(7710),R=n(47037),M=n.n(R),D=n(23279),L=n.n(D),B=n(36968),F=n.n(B),U=n(90242);const z="spec_update_spec",q="spec_update_url",$="spec_update_json",V="spec_update_param",H="spec_update_empty_param_inclusion",W="spec_validate_param",J="spec_set_response",K="spec_set_request",G="spec_set_mutated_request",Z="spec_log_request",Y="spec_clear_response",Q="spec_clear_request",X="spec_clear_validate_param",ee="spec_update_operation_meta_value",te="spec_update_resolved",ne="spec_update_resolved_subtree",re="set_scheme",oe=e=>M()(e)?e:"";function ae(e){const t=oe(e).replace(/\t/g," ");if("string"==typeof e)return{type:z,payload:t}}function ie(e){return{type:te,payload:e}}function se(e){return{type:q,payload:e}}function le(e){return{type:$,payload:e}}const ue=e=>t=>{let{specActions:n,specSelectors:r,errActions:o}=t,{specStr:a}=r,i=null;try{e=e||a(),o.clear({source:"parser"}),i=j.ZP.load(e,{schema:j.A8})}catch(e){return console.error(e),o.newSpecErr({source:"parser",level:"error",message:e.reason,line:e.mark&&e.mark.line?e.mark.line+1:void 0})}return i&&"object"==typeof i?n.updateJsonSpec(i):{}};let ce=!1;const pe=(e,t)=>n=>{let{specActions:r,specSelectors:a,errActions:s,fn:{fetch:u,resolve:c,AST:p={}},getConfigs:f}=n;ce||(console.warn("specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!"),ce=!0);const{modelPropertyMacro:h,parameterMacro:d,requestInterceptor:m,responseInterceptor:g}=f();void 0===e&&(e=a.specJson()),void 0===t&&(t=a.url());let y=p.getLineNumberForPath?p.getLineNumberForPath:()=>{},v=a.specStr();return c({fetch:u,spec:e,baseDoc:t,modelPropertyMacro:h,parameterMacro:d,requestInterceptor:m,responseInterceptor:g}).then((e=>{let{spec:t,errors:n}=e;if(s.clear({type:"thrown"}),o()(n)&&n.length>0){let e=i()(n).call(n,(e=>(console.error(e),e.line=e.fullPath?y(v,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",l()(e,"message",{enumerable:!0,value:e.message}),e)));s.newThrownErrBatch(e)}return r.updateResolved(t)}))};let fe=[];const he=L()((async()=>{const e=fe.system;if(!e)return void console.error("debResolveSubtrees: don't have a system to operate on, aborting.");const{errActions:t,errSelectors:n,fn:{resolveSubtree:r,fetch:a,AST:s={}},specSelectors:u,specActions:p}=e;if(!r)return void console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing.");let h=s.getLineNumberForPath?s.getLineNumberForPath:()=>{};const m=u.specStr(),{modelPropertyMacro:y,parameterMacro:b,requestInterceptor:w,responseInterceptor:E}=e.getConfigs();try{var x=await c()(fe).call(fe,(async(e,s)=>{const{resultMap:c,specWithCurrentSubtrees:p}=await e,{errors:x,spec:_}=await r(p,s,{baseDoc:u.url(),modelPropertyMacro:y,parameterMacro:b,requestInterceptor:w,responseInterceptor:E});if(n.allErrors().size&&t.clearBy((e=>{var t;return"thrown"!==e.get("type")||"resolver"!==e.get("source")||!f()(t=e.get("fullPath")).call(t,((e,t)=>e===s[t]||void 0===s[t]))})),o()(x)&&x.length>0){let e=i()(x).call(x,(e=>(e.line=e.fullPath?h(m,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",l()(e,"message",{enumerable:!0,value:e.message}),e)));t.newThrownErrBatch(e)}var S,A;_&&u.isOAS3()&&"components"===s[0]&&"securitySchemes"===s[1]&&await d().all(i()(S=g()(A=v()(_)).call(A,(e=>"openIdConnect"===e.type))).call(S,(async e=>{const t={url:e.openIdConnectUrl,requestInterceptor:w,responseInterceptor:E};try{const n=await a(t);n instanceof Error||n.status>=400?console.error(n.statusText+" "+t.url):e.openIdConnectData=JSON.parse(n.text)}catch(e){console.error(e)}})));return F()(c,s,_),F()(p,s,_),{resultMap:c,specWithCurrentSubtrees:p}}),d().resolve({resultMap:(u.specResolvedSubtree([])||(0,T.Map)()).toJS(),specWithCurrentSubtrees:u.specJson().toJS()}));delete fe.system,fe=[]}catch(e){console.error(e)}p.updateResolvedSubtree([],x.resultMap)}),35),de=e=>t=>{var n;w()(n=i()(fe).call(fe,(e=>e.join("@@")))).call(n,e.join("@@"))>-1||(fe.push(e),fe.system=t,he())};function me(e,t,n,r,o){return{type:V,payload:{path:e,value:r,paramName:t,paramIn:n,isXml:o}}}function ge(e,t,n,r){return{type:V,payload:{path:e,param:t,value:n,isXml:r}}}const ye=(e,t)=>({type:ne,payload:{path:e,value:t}}),ve=()=>({type:ne,payload:{path:[],value:(0,T.Map)()}}),be=(e,t)=>({type:W,payload:{pathMethod:e,isOAS3:t}}),we=(e,t,n,r)=>({type:H,payload:{pathMethod:e,paramName:t,paramIn:n,includeEmptyValue:r}});function Ee(e){return{type:X,payload:{pathMethod:e}}}function xe(e,t){return{type:ee,payload:{path:e,value:t,key:"consumes_value"}}}function _e(e,t){return{type:ee,payload:{path:e,value:t,key:"produces_value"}}}const Se=(e,t,n)=>({payload:{path:e,method:t,res:n},type:J}),Ae=(e,t,n)=>({payload:{path:e,method:t,req:n},type:K}),Ce=(e,t,n)=>({payload:{path:e,method:t,req:n},type:G}),ke=e=>({payload:e,type:Z}),Oe=e=>t=>{let{fn:n,specActions:r,specSelectors:a,getConfigs:s,oas3Selectors:l}=t,{pathName:u,method:c,operation:p}=e,{requestInterceptor:f,responseInterceptor:h}=s(),d=p.toJS();var m,y;p&&p.get("parameters")&&x()(m=g()(y=p.get("parameters")).call(y,(e=>e&&!0===e.get("allowEmptyValue")))).call(m,(t=>{if(a.parameterInclusionSettingFor([u,c],t.get("name"),t.get("in"))){e.parameters=e.parameters||{};const n=(0,U.cz)(t,e.parameters);(!n||n&&0===n.size)&&(e.parameters[t.get("name")]="")}}));if(e.contextUrl=N()(a.url()).toString(),d&&d.operationId?e.operationId=d.operationId:d&&u&&c&&(e.operationId=n.opId(d,u,c)),a.isOAS3()){const t=`${u}:${c}`;e.server=l.selectedServer(t)||l.selectedServer();const n=l.serverVariables({server:e.server,namespace:t}).toJS(),r=l.serverVariables({server:e.server}).toJS();e.serverVariables=S()(n).length?n:r,e.requestContentType=l.requestContentType(u,c),e.responseContentType=l.responseContentType(u,c)||"*/*";const a=l.requestBodyValue(u,c),s=l.requestBodyInclusionSetting(u,c);var v;if(a&&a.toJS)e.requestBody=g()(v=i()(a).call(a,(e=>T.Map.isMap(e)?e.get("value"):e))).call(v,((e,t)=>(o()(e)?0!==e.length:!(0,U.O2)(e))||s.get(t))).toJS();else e.requestBody=a}let b=C()({},e);b=n.buildRequest(b),r.setRequest(e.pathName,e.method,b);e.requestInterceptor=async t=>{let n=await f.apply(void 0,[t]),o=C()({},n);return r.setMutatedRequest(e.pathName,e.method,o),n},e.responseInterceptor=h;const w=O()();return n.execute(e).then((t=>{t.duration=O()()-w,r.setResponse(e.pathName,e.method,t)})).catch((t=>{"Failed to fetch"===t.message&&(t.name="",t.message='**Failed to fetch.** \n**Possible Reasons:** \n - CORS \n - Network Failure \n - URL scheme must be "http" or "https" for CORS request.'),r.setResponse(e.pathName,e.method,{error:!0,err:(0,P.serializeError)(t)})}))},je=function(){let{path:e,method:t,...n}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return r=>{let{fn:{fetch:o},specSelectors:a,specActions:i}=r,s=a.specJsonWithResolvedSubtrees().toJS(),l=a.operationScheme(e,t),{requestContentType:u,responseContentType:c}=a.contentTypeValues([e,t]).toJS(),p=/xml/i.test(u),f=a.parameterValues([e,t],p).toJS();return i.executeRequest({...n,fetch:o,spec:s,pathName:e,method:t,parameters:f,requestContentType:u,scheme:l,responseContentType:c})}};function Te(e,t){return{type:Y,payload:{path:e,method:t}}}function Ie(e,t){return{type:Q,payload:{path:e,method:t}}}function Ne(e,t,n){return{type:re,payload:{scheme:e,path:t,method:n}}}},37038:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(20032),o=n(51228),a=n(33881),i=n(77508);function s(){return{statePlugins:{spec:{wrapActions:i,reducers:r.default,actions:o,selectors:a}}}}},20032:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>d});var r=n(24282),o=n.n(r),a=n(97606),i=n.n(a),s=n(76986),l=n.n(s),u=n(43393),c=n(90242),p=n(27504),f=n(33881),h=n(51228);const d={[h.UPDATE_SPEC]:(e,t)=>"string"==typeof t.payload?e.set("spec",t.payload):e,[h.UPDATE_URL]:(e,t)=>e.set("url",t.payload+""),[h.UPDATE_JSON]:(e,t)=>e.set("json",(0,c.oG)(t.payload)),[h.UPDATE_RESOLVED]:(e,t)=>e.setIn(["resolved"],(0,c.oG)(t.payload)),[h.UPDATE_RESOLVED_SUBTREE]:(e,t)=>{const{value:n,path:r}=t.payload;return e.setIn(["resolvedSubtrees",...r],(0,c.oG)(n))},[h.UPDATE_PARAM]:(e,t)=>{let{payload:n}=t,{path:r,paramName:o,paramIn:a,param:i,value:s,isXml:l}=n,u=i?(0,c.V9)(i):`${a}.${o}`;const p=l?"value_xml":"value";return e.setIn(["meta","paths",...r,"parameters",u,p],s)},[h.UPDATE_EMPTY_PARAM_INCLUSION]:(e,t)=>{let{payload:n}=t,{pathMethod:r,paramName:o,paramIn:a,includeEmptyValue:i}=n;if(!o||!a)return console.warn("Warning: UPDATE_EMPTY_PARAM_INCLUSION could not generate a paramKey."),e;const s=`${a}.${o}`;return e.setIn(["meta","paths",...r,"parameter_inclusions",s],i)},[h.VALIDATE_PARAMS]:(e,t)=>{let{payload:{pathMethod:n,isOAS3:r}}=t;const a=(0,f.specJsonWithResolvedSubtrees)(e).getIn(["paths",...n]),i=(0,f.parameterValues)(e,n).toJS();return e.updateIn(["meta","paths",...n,"parameters"],(0,u.fromJS)({}),(t=>{var s;return o()(s=a.get("parameters",(0,u.List)())).call(s,((t,o)=>{const a=(0,c.cz)(o,i),s=(0,f.parameterInclusionSettingFor)(e,n,o.get("name"),o.get("in")),l=(0,c.Ik)(o,a,{bypassRequiredCheck:s,isOAS3:r});return t.setIn([(0,c.V9)(o),"errors"],(0,u.fromJS)(l))}),t)}))},[h.CLEAR_VALIDATE_PARAMS]:(e,t)=>{let{payload:{pathMethod:n}}=t;return e.updateIn(["meta","paths",...n,"parameters"],(0,u.fromJS)([]),(e=>i()(e).call(e,(e=>e.set("errors",(0,u.fromJS)([]))))))},[h.SET_RESPONSE]:(e,t)=>{let n,{payload:{res:r,path:o,method:a}}=t;n=r.error?l()({error:!0,name:r.err.name,message:r.err.message,statusCode:r.err.statusCode},r.err.response):r,n.headers=n.headers||{};let i=e.setIn(["responses",o,a],(0,c.oG)(n));return p.Z.Blob&&r.data instanceof p.Z.Blob&&(i=i.setIn(["responses",o,a,"text"],r.data)),i},[h.SET_REQUEST]:(e,t)=>{let{payload:{req:n,path:r,method:o}}=t;return e.setIn(["requests",r,o],(0,c.oG)(n))},[h.SET_MUTATED_REQUEST]:(e,t)=>{let{payload:{req:n,path:r,method:o}}=t;return e.setIn(["mutatedRequests",r,o],(0,c.oG)(n))},[h.UPDATE_OPERATION_META_VALUE]:(e,t)=>{let{payload:{path:n,value:r,key:o}}=t,a=["paths",...n],i=["meta","paths",...n];return e.getIn(["json",...a])||e.getIn(["resolved",...a])||e.getIn(["resolvedSubtrees",...a])?e.setIn([...i,o],(0,u.fromJS)(r)):e},[h.CLEAR_RESPONSE]:(e,t)=>{let{payload:{path:n,method:r}}=t;return e.deleteIn(["responses",n,r])},[h.CLEAR_REQUEST]:(e,t)=>{let{payload:{path:n,method:r}}=t;return e.deleteIn(["requests",n,r])},[h.SET_SCHEME]:(e,t)=>{let{payload:{scheme:n,path:r,method:o}}=t;return r&&o?e.setIn(["scheme",r,o],n):r||o?void 0:e.setIn(["scheme","_defaultScheme"],n)}}},33881:(e,t,n)=>{"use strict";n.r(t),n.d(t,{allowTryItOutFor:()=>pe,basePath:()=>Y,canExecuteScheme:()=>Ce,consumes:()=>H,consumesOptionsFor:()=>Se,contentTypeValues:()=>Ee,currentProducesFor:()=>xe,definitions:()=>Z,externalDocs:()=>U,findDefinition:()=>G,getOAS3RequiredRequestBodyContentType:()=>je,getParameter:()=>ge,hasHost:()=>ye,host:()=>Q,info:()=>F,isMediaTypeSchemaPropertiesEqual:()=>Te,isOAS3:()=>B,lastError:()=>O,mutatedRequestFor:()=>ce,mutatedRequests:()=>se,operationScheme:()=>Ae,operationWithMeta:()=>me,operations:()=>V,operationsWithRootInherited:()=>ee,operationsWithTags:()=>re,parameterInclusionSettingFor:()=>he,parameterValues:()=>ve,parameterWithMeta:()=>de,parameterWithMetaByIdentity:()=>fe,parametersIncludeIn:()=>be,parametersIncludeType:()=>we,paths:()=>$,produces:()=>W,producesOptionsFor:()=>_e,requestFor:()=>ue,requests:()=>ie,responseFor:()=>le,responses:()=>ae,schemes:()=>X,security:()=>J,securityDefinitions:()=>K,semver:()=>q,spec:()=>L,specJson:()=>N,specJsonWithResolvedSubtrees:()=>D,specResolved:()=>P,specResolvedSubtree:()=>R,specSource:()=>I,specStr:()=>T,tagDetails:()=>ne,taggedOperations:()=>oe,tags:()=>te,url:()=>j,validateBeforeExecute:()=>Oe,validationErrors:()=>ke,version:()=>z});var r=n(24278),o=n.n(r),a=n(86),i=n.n(a),s=n(11882),l=n.n(s),u=n(97606),c=n.n(u),p=n(14418),f=n.n(p),h=n(51679),d=n.n(h),m=n(24282),g=n.n(m),y=n(2578),v=n.n(y),b=n(92039),w=n.n(b),E=n(58309),x=n.n(E),_=n(20573),S=n(90242),A=n(43393);const C=["get","put","post","delete","options","head","patch","trace"],k=e=>e||(0,A.Map)(),O=(0,_.P1)(k,(e=>e.get("lastError"))),j=(0,_.P1)(k,(e=>e.get("url"))),T=(0,_.P1)(k,(e=>e.get("spec")||"")),I=(0,_.P1)(k,(e=>e.get("specSource")||"not-editor")),N=(0,_.P1)(k,(e=>e.get("json",(0,A.Map)()))),P=(0,_.P1)(k,(e=>e.get("resolved",(0,A.Map)()))),R=(e,t)=>e.getIn(["resolvedSubtrees",...t],void 0),M=(e,t)=>A.Map.isMap(e)&&A.Map.isMap(t)?t.get("$$ref")?t:(0,A.OrderedMap)().mergeWith(M,e,t):t,D=(0,_.P1)(k,(e=>(0,A.OrderedMap)().mergeWith(M,e.get("json"),e.get("resolvedSubtrees")))),L=e=>N(e),B=(0,_.P1)(L,(()=>!1)),F=(0,_.P1)(L,(e=>Ie(e&&e.get("info")))),U=(0,_.P1)(L,(e=>Ie(e&&e.get("externalDocs")))),z=(0,_.P1)(F,(e=>e&&e.get("version"))),q=(0,_.P1)(z,(e=>{var t;return o()(t=/v?([0-9]*)\.([0-9]*)\.([0-9]*)/i.exec(e)).call(t,1)})),$=(0,_.P1)(D,(e=>e.get("paths"))),V=(0,_.P1)($,(e=>{if(!e||e.size<1)return(0,A.List)();let t=(0,A.List)();return e&&i()(e)?(i()(e).call(e,((e,n)=>{if(!e||!i()(e))return{};i()(e).call(e,((e,r)=>{l()(C).call(C,r)<0||(t=t.push((0,A.fromJS)({path:n,method:r,operation:e,id:`${r}-${n}`})))}))})),t):(0,A.List)()})),H=(0,_.P1)(L,(e=>(0,A.Set)(e.get("consumes")))),W=(0,_.P1)(L,(e=>(0,A.Set)(e.get("produces")))),J=(0,_.P1)(L,(e=>e.get("security",(0,A.List)()))),K=(0,_.P1)(L,(e=>e.get("securityDefinitions"))),G=(e,t)=>{const n=e.getIn(["resolvedSubtrees","definitions",t],null),r=e.getIn(["json","definitions",t],null);return n||r||null},Z=(0,_.P1)(L,(e=>{const t=e.get("definitions");return A.Map.isMap(t)?t:(0,A.Map)()})),Y=(0,_.P1)(L,(e=>e.get("basePath"))),Q=(0,_.P1)(L,(e=>e.get("host"))),X=(0,_.P1)(L,(e=>e.get("schemes",(0,A.Map)()))),ee=(0,_.P1)(V,H,W,((e,t,n)=>c()(e).call(e,(e=>e.update("operation",(e=>{if(e){if(!A.Map.isMap(e))return;return e.withMutations((e=>(e.get("consumes")||e.update("consumes",(e=>(0,A.Set)(e).merge(t))),e.get("produces")||e.update("produces",(e=>(0,A.Set)(e).merge(n))),e)))}return(0,A.Map)()})))))),te=(0,_.P1)(L,(e=>{const t=e.get("tags",(0,A.List)());return A.List.isList(t)?f()(t).call(t,(e=>A.Map.isMap(e))):(0,A.List)()})),ne=(e,t)=>{var n;let r=te(e)||(0,A.List)();return d()(n=f()(r).call(r,A.Map.isMap)).call(n,(e=>e.get("name")===t),(0,A.Map)())},re=(0,_.P1)(ee,te,((e,t)=>g()(e).call(e,((e,t)=>{let n=(0,A.Set)(t.getIn(["operation","tags"]));return n.count()<1?e.update("default",(0,A.List)(),(e=>e.push(t))):g()(n).call(n,((e,n)=>e.update(n,(0,A.List)(),(e=>e.push(t)))),e)}),g()(t).call(t,((e,t)=>e.set(t.get("name"),(0,A.List)())),(0,A.OrderedMap)())))),oe=e=>t=>{var n;let{getConfigs:r}=t,{tagsSorter:o,operationsSorter:a}=r();return c()(n=re(e).sortBy(((e,t)=>t),((e,t)=>{let n="function"==typeof o?o:S.wh.tagsSorter[o];return n?n(e,t):null}))).call(n,((t,n)=>{let r="function"==typeof a?a:S.wh.operationsSorter[a],o=r?v()(t).call(t,r):t;return(0,A.Map)({tagDetails:ne(e,n),operations:o})}))},ae=(0,_.P1)(k,(e=>e.get("responses",(0,A.Map)()))),ie=(0,_.P1)(k,(e=>e.get("requests",(0,A.Map)()))),se=(0,_.P1)(k,(e=>e.get("mutatedRequests",(0,A.Map)()))),le=(e,t,n)=>ae(e).getIn([t,n],null),ue=(e,t,n)=>ie(e).getIn([t,n],null),ce=(e,t,n)=>se(e).getIn([t,n],null),pe=()=>!0,fe=(e,t,n)=>{const r=D(e).getIn(["paths",...t,"parameters"],(0,A.OrderedMap)()),o=e.getIn(["meta","paths",...t,"parameters"],(0,A.OrderedMap)()),a=c()(r).call(r,(e=>{const t=o.get(`${n.get("in")}.${n.get("name")}`),r=o.get(`${n.get("in")}.${n.get("name")}.hash-${n.hashCode()}`);return(0,A.OrderedMap)().merge(e,t,r)}));return d()(a).call(a,(e=>e.get("in")===n.get("in")&&e.get("name")===n.get("name")),(0,A.OrderedMap)())},he=(e,t,n,r)=>{const o=`${r}.${n}`;return e.getIn(["meta","paths",...t,"parameter_inclusions",o],!1)},de=(e,t,n,r)=>{const o=D(e).getIn(["paths",...t,"parameters"],(0,A.OrderedMap)()),a=d()(o).call(o,(e=>e.get("in")===r&&e.get("name")===n),(0,A.OrderedMap)());return fe(e,t,a)},me=(e,t,n)=>{var r;const o=D(e).getIn(["paths",t,n],(0,A.OrderedMap)()),a=e.getIn(["meta","paths",t,n],(0,A.OrderedMap)()),i=c()(r=o.get("parameters",(0,A.List)())).call(r,(r=>fe(e,[t,n],r)));return(0,A.OrderedMap)().merge(o,a).set("parameters",i)};function ge(e,t,n,r){t=t||[];let o=e.getIn(["meta","paths",...t,"parameters"],(0,A.fromJS)([]));return d()(o).call(o,(e=>A.Map.isMap(e)&&e.get("name")===n&&e.get("in")===r))||(0,A.Map)()}const ye=(0,_.P1)(L,(e=>{const t=e.get("host");return"string"==typeof t&&t.length>0&&"/"!==t[0]}));function ve(e,t,n){t=t||[];let r=me(e,...t).get("parameters",(0,A.List)());return g()(r).call(r,((e,t)=>{let r=n&&"body"===t.get("in")?t.get("value_xml"):t.get("value");return e.set((0,S.V9)(t,{allowHashes:!1}),r)}),(0,A.fromJS)({}))}function be(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(A.List.isList(e))return w()(e).call(e,(e=>A.Map.isMap(e)&&e.get("in")===t))}function we(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(A.List.isList(e))return w()(e).call(e,(e=>A.Map.isMap(e)&&e.get("type")===t))}function Ee(e,t){t=t||[];let n=D(e).getIn(["paths",...t],(0,A.fromJS)({})),r=e.getIn(["meta","paths",...t],(0,A.fromJS)({})),o=xe(e,t);const a=n.get("parameters")||new A.List,i=r.get("consumes_value")?r.get("consumes_value"):we(a,"file")?"multipart/form-data":we(a,"formData")?"application/x-www-form-urlencoded":void 0;return(0,A.fromJS)({requestContentType:i,responseContentType:o})}function xe(e,t){t=t||[];const n=D(e).getIn(["paths",...t],null);if(null===n)return;const r=e.getIn(["meta","paths",...t,"produces_value"],null),o=n.getIn(["produces",0],null);return r||o||"application/json"}function _e(e,t){t=t||[];const n=D(e),r=n.getIn(["paths",...t],null);if(null===r)return;const[o]=t,a=r.get("produces",null),i=n.getIn(["paths",o,"produces"],null),s=n.getIn(["produces"],null);return a||i||s}function Se(e,t){t=t||[];const n=D(e),r=n.getIn(["paths",...t],null);if(null===r)return;const[o]=t,a=r.get("consumes",null),i=n.getIn(["paths",o,"consumes"],null),s=n.getIn(["consumes"],null);return a||i||s}const Ae=(e,t,n)=>{let r=e.get("url").match(/^([a-z][a-z0-9+\-.]*):/),o=x()(r)?r[1]:null;return e.getIn(["scheme",t,n])||e.getIn(["scheme","_defaultScheme"])||o||""},Ce=(e,t,n)=>{var r;return l()(r=["http","https"]).call(r,Ae(e,t,n))>-1},ke=(e,t)=>{t=t||[];let n=e.getIn(["meta","paths",...t,"parameters"],(0,A.fromJS)([]));const r=[];return i()(n).call(n,(e=>{let t=e.get("errors");t&&t.count()&&i()(t).call(t,(e=>r.push(e)))})),r},Oe=(e,t)=>0===ke(e,t).length,je=(e,t)=>{var n;let r={requestBody:!1,requestContentType:{}},o=e.getIn(["resolvedSubtrees","paths",...t,"requestBody"],(0,A.fromJS)([]));return o.size<1||(o.getIn(["required"])&&(r.requestBody=o.getIn(["required"])),i()(n=o.getIn(["content"]).entrySeq()).call(n,(e=>{const t=e[0];if(e[1].getIn(["schema","required"])){const n=e[1].getIn(["schema","required"]).toJS();r.requestContentType[t]=n}}))),r},Te=(e,t,n,r)=>{if((n||r)&&n===r)return!0;let o=e.getIn(["resolvedSubtrees","paths",...t,"requestBody","content"],(0,A.fromJS)([]));if(o.size<2||!n||!r)return!1;let a=o.getIn([n,"schema","properties"],(0,A.fromJS)([])),i=o.getIn([r,"schema","properties"],(0,A.fromJS)([]));return!!a.equals(i)};function Ie(e){return A.Map.isMap(e)?e:new A.Map}},77508:(e,t,n)=>{"use strict";n.r(t),n.d(t,{executeRequest:()=>p,updateJsonSpec:()=>c,updateSpec:()=>u,validateParams:()=>f});var r=n(28222),o=n.n(r),a=n(86),i=n.n(a),s=n(27361),l=n.n(s);const u=(e,t)=>{let{specActions:n}=t;return function(){e(...arguments),n.parseToJson(...arguments)}},c=(e,t)=>{let{specActions:n}=t;return function(){for(var t=arguments.length,r=new Array(t),a=0;a{l()(u,[e]).$ref&&n.requestResolvedSubtree(["paths",e])})),n.requestResolvedSubtree(["components","securitySchemes"])}},p=(e,t)=>{let{specActions:n}=t;return t=>(n.logRequest(t),e(t))},f=(e,t)=>{let{specSelectors:n}=t;return t=>e(t,n.isOAS3())}},34852:(e,t,n)=>{"use strict";n.r(t),n.d(t,{loaded:()=>r});const r=(e,t)=>function(){e(...arguments);const n=t.getConfigs().withCredentials;void 0!==n&&(t.fn.fetch.withCredentials="string"==typeof n?"true"===n:!!n)}},74370:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>on});var r={};n.r(r),n.d(r,{JsonPatchError:()=>G,_areEquals:()=>ae,applyOperation:()=>ee,applyPatch:()=>te,applyReducer:()=>ne,deepClone:()=>Z,getValueByPointer:()=>X,validate:()=>oe,validator:()=>re});var o={};n.r(o),n.d(o,{compare:()=>he,generate:()=>pe,observe:()=>ce,unobserve:()=>ue});var a={};n.r(a),n.d(a,{cookie:()=>Ft,header:()=>Bt,path:()=>Mt,query:()=>Dt});var i=n(58826),s=n.n(i);const l="application/json, application/yaml";function u(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{requestInterceptor:n,responseInterceptor:r}=t,o=e.withCredentials?"include":"same-origin";return t=>e({url:t,loadSpec:!0,requestInterceptor:n,responseInterceptor:r,headers:{Accept:l},credentials:o}).then((e=>e.body))}n(31905);var c=n(80129),p=n.n(c),f=n(1272);const h="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:window,{FormData:d,Blob:m,File:g}=h,y=e=>":/?#[]@!$&'()*+,;=".indexOf(e)>-1,v=e=>/^[a-z0-9\-._~]+$/i.test(e);function b(e){let{escape:t}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return"number"==typeof e&&(e=e.toString()),"string"==typeof e&&e.length&&t?n?JSON.parse(e):[...e].map((e=>{if(v(e))return e;if(y(e)&&"unsafe"===t)return e;const n=new TextEncoder;return Array.from(n.encode(e)).map((e=>`0${e.toString(16).toUpperCase()}`.slice(-2))).map((e=>`%${e}`)).join("")})).join(""):e}function w(e){const{value:t}=e;return Array.isArray(t)?function(e){let{key:t,value:n,style:r,explode:o,escape:a}=e;const i=e=>b(e,{escape:a});if("simple"===r)return n.map((e=>i(e))).join(",");if("label"===r)return`.${n.map((e=>i(e))).join(".")}`;if("matrix"===r)return n.map((e=>i(e))).reduce(((e,n)=>!e||o?`${e||""};${t}=${n}`:`${e},${n}`),"");if("form"===r){const e=o?`&${t}=`:",";return n.map((e=>i(e))).join(e)}if("spaceDelimited"===r){const e=o?`${t}=`:"";return n.map((e=>i(e))).join(` ${e}`)}if("pipeDelimited"===r){const e=o?`${t}=`:"";return n.map((e=>i(e))).join(`|${e}`)}return}(e):"object"==typeof t?function(e){let{key:t,value:n,style:r,explode:o,escape:a}=e;const i=e=>b(e,{escape:a}),s=Object.keys(n);if("simple"===r)return s.reduce(((e,t)=>{const r=i(n[t]);return`${e?`${e},`:""}${t}${o?"=":","}${r}`}),"");if("label"===r)return s.reduce(((e,t)=>{const r=i(n[t]);return`${e?`${e}.`:"."}${t}${o?"=":"."}${r}`}),"");if("matrix"===r&&o)return s.reduce(((e,t)=>`${e?`${e};`:";"}${t}=${i(n[t])}`),"");if("matrix"===r)return s.reduce(((e,r)=>{const o=i(n[r]);return`${e?`${e},`:`;${t}=`}${r},${o}`}),"");if("form"===r)return s.reduce(((e,t)=>{const r=i(n[t]);return`${e?`${e}${o?"&":","}`:""}${t}${o?"=":","}${r}`}),"");return}(e):function(e){let{key:t,value:n,style:r,escape:o}=e;const a=e=>b(e,{escape:o});if("simple"===r)return a(n);if("label"===r)return`.${a(n)}`;if("matrix"===r)return`;${t}=${a(n)}`;if("form"===r)return a(n);if("deepObject"===r)return a(n,{},!0);return}(e)}const E=(e,t)=>{t.body=e},x={serializeRes:A,mergeInQueryOrForm:R};async function _(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"object"==typeof e&&(t=e,e=t.url),t.headers=t.headers||{},x.mergeInQueryOrForm(t),t.headers&&Object.keys(t.headers).forEach((e=>{const n=t.headers[e];"string"==typeof n&&(t.headers[e]=n.replace(/\n+/g," "))})),t.requestInterceptor&&(t=await t.requestInterceptor(t)||t);const n=t.headers["content-type"]||t.headers["Content-Type"];let r;/multipart\/form-data/i.test(n)&&t.body instanceof d&&(delete t.headers["content-type"],delete t.headers["Content-Type"]);try{r=await(t.userFetch||fetch)(t.url,t),r=await x.serializeRes(r,e,t),t.responseInterceptor&&(r=await t.responseInterceptor(r)||r)}catch(e){if(!r)throw e;const t=new Error(r.statusText||`response status is ${r.status}`);throw t.status=r.status,t.statusCode=r.status,t.responseError=e,t}if(!r.ok){const e=new Error(r.statusText||`response status is ${r.status}`);throw e.status=r.status,e.statusCode=r.status,e.response=r,e}return r}const S=function(){return/(json|xml|yaml|text)\b/.test(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"")};function A(e,t){let{loadSpec:n=!1}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const r={ok:e.ok,url:e.url||t,status:e.status,statusText:e.statusText,headers:C(e.headers)},o=r.headers["content-type"],a=n||S(o);return(a?e.text:e.blob||e.buffer).call(e).then((e=>{if(r.text=e,r.data=e,a)try{const t=function(e,t){return t&&(0===t.indexOf("application/json")||t.indexOf("+json")>0)?JSON.parse(e):f.ZP.load(e)}(e,o);r.body=t,r.obj=t}catch(e){r.parseError=e}return r}))}function C(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return"function"!=typeof e.entries?{}:Array.from(e.entries()).reduce(((e,t)=>{let[n,r]=t;return e[n]=function(e){return e.includes(", ")?e.split(", "):e}(r),e}),{})}function k(e,t){return t||"undefined"==typeof navigator||(t=navigator),t&&"ReactNative"===t.product?!(!e||"object"!=typeof e||"string"!=typeof e.uri):void 0!==g&&e instanceof g||(void 0!==m&&e instanceof m||(!!ArrayBuffer.isView(e)||null!==e&&"object"==typeof e&&"function"==typeof e.pipe))}function O(e,t){return Array.isArray(e)&&e.some((e=>k(e,t)))}const j={form:",",spaceDelimited:"%20",pipeDelimited:"|"},T={csv:",",ssv:"%20",tsv:"%09",pipes:"|"};function I(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];const{collectionFormat:r,allowEmptyValue:o,serializationOption:a,encoding:i}=t,s="object"!=typeof t||Array.isArray(t)?t:t.value,l=n?e=>e.toString():e=>encodeURIComponent(e),u=l(e);if(void 0===s&&o)return[[u,""]];if(k(s)||O(s))return[[u,s]];if(a)return N(e,s,n,a);if(i){if([typeof i.style,typeof i.explode,typeof i.allowReserved].some((e=>"undefined"!==e))){const{style:t,explode:r,allowReserved:o}=i;return N(e,s,n,{style:t,explode:r,allowReserved:o})}if(i.contentType){if("application/json"===i.contentType){return[[u,l("string"==typeof s?s:JSON.stringify(s))]]}return[[u,l(s.toString())]]}return"object"!=typeof s?[[u,l(s)]]:Array.isArray(s)&&s.every((e=>"object"!=typeof e))?[[u,s.map(l).join(",")]]:[[u,l(JSON.stringify(s))]]}return"object"!=typeof s?[[u,l(s)]]:Array.isArray(s)?"multi"===r?[[u,s.map(l)]]:[[u,s.map(l).join(T[r||"csv"])]]:[[u,""]]}function N(e,t,n,r){const o=r.style||"form",a=void 0===r.explode?"form"===o:r.explode,i=!n&&(r&&r.allowReserved?"unsafe":"reserved"),s=e=>b(e,{escape:i}),l=n?e=>e:e=>b(e,{escape:i});return"object"!=typeof t?[[l(e),s(t)]]:Array.isArray(t)?a?[[l(e),t.map(s)]]:[[l(e),t.map(s).join(j[o])]]:"deepObject"===o?Object.keys(t).map((n=>[l(`${e}[${n}]`),s(t[n])])):a?Object.keys(t).map((e=>[l(e),s(t[e])])):[[l(e),Object.keys(t).map((e=>[`${l(e)},${s(t[e])}`])).join(",")]]}function P(e){const t=Object.keys(e).reduce(((t,n)=>{for(const[r,o]of I(n,e[n]))t[r]=o;return t}),{});return p().stringify(t,{encode:!1,indices:!1})||""}function R(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const{url:t="",query:n,form:r}=e;if(r){const t=Object.keys(r).some((e=>{const{value:t}=r[e];return k(t)||O(t)})),n=e.headers["content-type"]||e.headers["Content-Type"];if(t||/multipart\/form-data/i.test(n)){const t=(o=e.form,Object.entries(o).reduce(((e,t)=>{let[n,r]=t;for(const[t,o]of I(n,r,!0))if(Array.isArray(o))for(const n of o)if(ArrayBuffer.isView(n)){const r=new m([n]);e.append(t,r)}else e.append(t,n);else if(ArrayBuffer.isView(o)){const n=new m([o]);e.append(t,n)}else e.append(t,o);return e}),new d));E(t,e)}else e.body=P(r);delete e.form}var o;if(n){const[r,o]=t.split("?");let a="";if(o){const e=p().parse(o);Object.keys(n).forEach((t=>delete e[t])),a=p().stringify(e,{encode:!0})}const i=function(){for(var e=arguments.length,t=new Array(e),n=0;ne)).join("&");return r?`?${r}`:""}(a,P(n));e.url=r+i,delete e.query}return e}const M=e=>{const{baseDoc:t,url:n}=e;return t||n},D=e=>{const{fetch:t,http:n}=e;return t||n||_};var L,B=(L=function(e,t){return L=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])},L(e,t)},function(e,t){function n(){this.constructor=e}L(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),F=Object.prototype.hasOwnProperty;function U(e,t){return F.call(e,t)}function z(e){if(Array.isArray(e)){for(var t=new Array(e.length),n=0;n=48&&t<=57))return!1;n++}return!0}function V(e){return-1===e.indexOf("/")&&-1===e.indexOf("~")?e:e.replace(/~/g,"~0").replace(/\//g,"~1")}function H(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")}function W(e){if(void 0===e)return!0;if(e)if(Array.isArray(e)){for(var t=0,n=e.length;t0&&"constructor"==s[u-1]))throw new TypeError("JSON-Patch: modifying `__proto__` or `constructor/prototype` prop is banned for security reasons, if this was on purpose, please set `banPrototypeModifications` flag false and pass it to this function. More info in fast-json-patch README");if(n&&void 0===p&&(void 0===l[f]?p=s.slice(0,u).join("/"):u==c-1&&(p=t.path),void 0!==p&&h(t,0,e,p)),u++,Array.isArray(l)){if("-"===f)f=l.length;else{if(n&&!$(f))throw new G("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index","OPERATION_PATH_ILLEGAL_ARRAY_INDEX",a,t,e);$(f)&&(f=~~f)}if(u>=c){if(n&&"add"===t.op&&f>l.length)throw new G("The specified index MUST NOT be greater than the number of elements in the array","OPERATION_VALUE_OUT_OF_BOUNDS",a,t,e);if(!1===(i=Q[t.op].call(t,l,f,e)).test)throw new G("Test operation failed","TEST_OPERATION_FAILED",a,t,e);return i}}else if(u>=c){if(!1===(i=Y[t.op].call(t,l,f,e)).test)throw new G("Test operation failed","TEST_OPERATION_FAILED",a,t,e);return i}if(l=l[f],n&&u0)throw new G('Operation `path` property must start with "/"',"OPERATION_PATH_INVALID",t,e,n);if(("move"===e.op||"copy"===e.op)&&"string"!=typeof e.from)throw new G("Operation `from` property is not present (applicable in `move` and `copy` operations)","OPERATION_FROM_REQUIRED",t,e,n);if(("add"===e.op||"replace"===e.op||"test"===e.op)&&void 0===e.value)throw new G("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_REQUIRED",t,e,n);if(("add"===e.op||"replace"===e.op||"test"===e.op)&&W(e.value))throw new G("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED",t,e,n);if(n)if("add"==e.op){var o=e.path.split("/").length,a=r.split("/").length;if(o!==a+1&&o!==a)throw new G("Cannot perform an `add` operation at the desired path","OPERATION_PATH_CANNOT_ADD",t,e,n)}else if("replace"===e.op||"remove"===e.op||"_get"===e.op){if(e.path!==r)throw new G("Cannot perform the operation at a path that does not exist","OPERATION_PATH_UNRESOLVABLE",t,e,n)}else if("move"===e.op||"copy"===e.op){var i=oe([{op:"_get",path:e.from,value:void 0}],n);if(i&&"OPERATION_PATH_UNRESOLVABLE"===i.name)throw new G("Cannot perform the operation from a path that does not exist","OPERATION_FROM_UNRESOLVABLE",t,e,n)}}function oe(e,t,n){try{if(!Array.isArray(e))throw new G("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");if(t)te(q(t),q(e),n||!0);else{n=n||re;for(var r=0;r0&&(e.patches=[],e.callback&&e.callback(r)),r}function fe(e,t,n,r,o){if(t!==e){"function"==typeof t.toJSON&&(t=t.toJSON());for(var a=z(t),i=z(e),s=!1,l=i.length-1;l>=0;l--){var u=e[p=i[l]];if(!U(t,p)||void 0===t[p]&&void 0!==u&&!1===Array.isArray(t))Array.isArray(e)===Array.isArray(t)?(o&&n.push({op:"test",path:r+"/"+V(p),value:q(u)}),n.push({op:"remove",path:r+"/"+V(p)}),s=!0):(o&&n.push({op:"test",path:r,value:e}),n.push({op:"replace",path:r,value:t}),!0);else{var c=t[p];"object"==typeof u&&null!=u&&"object"==typeof c&&null!=c&&Array.isArray(u)===Array.isArray(c)?fe(u,c,n,r+"/"+V(p),o):u!==c&&(!0,o&&n.push({op:"test",path:r+"/"+V(p),value:q(u)}),n.push({op:"replace",path:r+"/"+V(p),value:q(c)}))}}if(s||a.length!=i.length)for(l=0;lvoid 0!==t&&e?e[t]:e),e)},applyPatch:function(e,t,n){if(n=n||{},"merge"===(t=s()(s()({},t),{},{path:t.path&&ye(t.path)})).op){const n=Ie(e,t.path);Object.assign(n,t.value),te(e,[ve(t.path,n)])}else if("mergeDeep"===t.op){const n=Ie(e,t.path),r=me()(n,t.value);e=te(e,[ve(t.path,r)]).newDocument}else if("add"===t.op&&""===t.path&&Ae(t.value)){te(e,Object.keys(t.value).reduce(((e,n)=>(e.push({op:"add",path:`/${ye(n)}`,value:t.value[n]}),e)),[]))}else if("replace"===t.op&&""===t.path){let{value:r}=t;n.allowMetaPatches&&t.meta&&je(t)&&(Array.isArray(t.value)||Ae(t.value))&&(r=s()(s()({},r),t.meta)),e=r}else if(te(e,[t]),n.allowMetaPatches&&t.meta&&je(t)&&(Array.isArray(t.value)||Ae(t.value))){const n=Ie(e,t.path),r=s()(s()({},n),t.meta);te(e,[ve(t.path,r)])}return e},parentPathMatch:function(e,t){if(!Array.isArray(t))return!1;for(let n=0,r=t.length;n(e+"").replace(/~/g,"~0").replace(/\//g,"~1"))).join("/")}`:e}function ve(e,t,n){return{op:"replace",path:e,value:t,meta:n}}function be(e,t,n){return Se(_e(e.filter(je).map((e=>t(e.value,n,e.path)))||[]))}function we(e,t,n){return n=n||[],Array.isArray(e)?e.map(((e,r)=>we(e,t,n.concat(r)))):Ae(e)?Object.keys(e).map((r=>we(e[r],t,n.concat(r)))):t(e,n[n.length-1],n)}function Ee(e,t,n){let r=[];if((n=n||[]).length>0){const o=t(e,n[n.length-1],n);o&&(r=r.concat(o))}if(Array.isArray(e)){const o=e.map(((e,r)=>Ee(e,t,n.concat(r))));o&&(r=r.concat(o))}else if(Ae(e)){const o=Object.keys(e).map((r=>Ee(e[r],t,n.concat(r))));o&&(r=r.concat(o))}return r=_e(r),r}function xe(e){return Array.isArray(e)?e:[e]}function _e(e){return[].concat(...e.map((e=>Array.isArray(e)?_e(e):e)))}function Se(e){return e.filter((e=>void 0!==e))}function Ae(e){return e&&"object"==typeof e}function Ce(e){return e&&"function"==typeof e}function ke(e){if(Te(e)){const{op:t}=e;return"add"===t||"remove"===t||"replace"===t}return!1}function Oe(e){return ke(e)||Te(e)&&"mutation"===e.type}function je(e){return Oe(e)&&("add"===e.op||"replace"===e.op||"merge"===e.op||"mergeDeep"===e.op)}function Te(e){return e&&"object"==typeof e}function Ie(e,t){try{return X(e,t)}catch(e){return console.error(e),{}}}var Ne=n(8575);function Pe(e,t){function n(){Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack;for(var e=arguments.length,n=new Array(e),r=0;r-1&&-1===Le.indexOf(n)||Be.indexOf(r)>-1||Fe.some((e=>r.indexOf(e)>-1))}function ze(e,t){const[n,r]=e.split("#"),o=Ne.resolve(n||"",t||"");return r?`${o}#${r}`:o}const qe=/^([a-z]+:\/\/|\/\/)/i,$e=Pe("JSONRefError",(function(e,t,n){this.originalError=n,Object.assign(this,t||{})})),Ve={},He=new WeakMap,We=[e=>"paths"===e[0]&&"responses"===e[3]&&"examples"===e[5],e=>"paths"===e[0]&&"responses"===e[3]&&"content"===e[5]&&"example"===e[7],e=>"paths"===e[0]&&"responses"===e[3]&&"content"===e[5]&&"examples"===e[7]&&"value"===e[9],e=>"paths"===e[0]&&"requestBody"===e[3]&&"content"===e[4]&&"example"===e[6],e=>"paths"===e[0]&&"requestBody"===e[3]&&"content"===e[4]&&"examples"===e[6]&&"value"===e[8],e=>"paths"===e[0]&&"parameters"===e[2]&&"example"===e[4],e=>"paths"===e[0]&&"parameters"===e[3]&&"example"===e[5],e=>"paths"===e[0]&&"parameters"===e[2]&&"examples"===e[4]&&"value"===e[6],e=>"paths"===e[0]&&"parameters"===e[3]&&"examples"===e[5]&&"value"===e[7],e=>"paths"===e[0]&&"parameters"===e[2]&&"content"===e[4]&&"example"===e[6],e=>"paths"===e[0]&&"parameters"===e[2]&&"content"===e[4]&&"examples"===e[6]&&"value"===e[8],e=>"paths"===e[0]&&"parameters"===e[3]&&"content"===e[4]&&"example"===e[7],e=>"paths"===e[0]&&"parameters"===e[3]&&"content"===e[5]&&"examples"===e[7]&&"value"===e[9]],Je={key:"$ref",plugin:(e,t,n,r)=>{const o=r.getInstance(),a=n.slice(0,-1);if(Ue(a)||(e=>We.some((t=>t(e))))(a))return;const{baseDoc:i}=r.getContext(n);if("string"!=typeof e)return new $e("$ref: must be a string (JSON-Ref)",{$ref:e,baseDoc:i,fullPath:n});const s=Qe(e),l=s[0],u=s[1]||"";let c,p,f;try{c=i||l?Ze(l,i):null}catch(t){return Ye(t,{pointer:u,$ref:e,basePath:c,fullPath:n})}if(function(e,t,n,r){let o=He.get(r);o||(o={},He.set(r,o));const a=function(e){if(0===e.length)return"";return`/${e.map(ot).join("/")}`}(n),i=`${t||""}#${e}`,s=a.replace(/allOf\/\d+\/?/g,""),l=r.contextTree.get([]).baseDoc;if(t===l&&it(s,e))return!0;let u="";const c=n.some((e=>(u=`${u}/${ot(e)}`,o[u]&&o[u].some((e=>it(e,i)||it(i,e))))));if(c)return!0;return void(o[s]=(o[s]||[]).concat(i))}(u,c,a,r)&&!o.useCircularStructures){const t=ze(e,c);return e===t?null:ge.replace(n,t)}if(null==c?(f=nt(u),p=r.get(f),void 0===p&&(p=new $e(`Could not resolve reference: ${e}`,{pointer:u,$ref:e,baseDoc:i,fullPath:n}))):(p=Xe(c,u),p=null!=p.__value?p.__value:p.catch((t=>{throw Ye(t,{pointer:u,$ref:e,baseDoc:i,fullPath:n})}))),p instanceof Error)return[ge.remove(n),p];const h=ze(e,c),d=ge.replace(a,p,{$$ref:h});if(c&&c!==i)return[d,ge.context(a,{baseDoc:c})];try{if(!function(e,t){const n=[e];return t.path.reduce(((e,t)=>(n.push(e[t]),e[t])),e),r(t.value);function r(e){return ge.isObject(e)&&(n.indexOf(e)>=0||Object.keys(e).some((t=>r(e[t]))))}}(r.state,d)||o.useCircularStructures)return d}catch(e){return null}}},Ke=Object.assign(Je,{docCache:Ve,absoluteify:Ze,clearCache:function(e){void 0!==e?delete Ve[e]:Object.keys(Ve).forEach((e=>{delete Ve[e]}))},JSONRefError:$e,wrapError:Ye,getDoc:et,split:Qe,extractFromDoc:Xe,fetchJSON:function(e){return fetch(e,{headers:{Accept:l},loadSpec:!0}).then((e=>e.text())).then((e=>f.ZP.load(e)))},extract:tt,jsonPointerToArray:nt,unescapeJsonPointerToken:rt}),Ge=Ke;function Ze(e,t){if(!qe.test(e)){if(!t)throw new $e(`Tried to resolve a relative URL, without having a basePath. path: '${e}' basePath: '${t}'`);return Ne.resolve(t,e)}return e}function Ye(e,t){let n;return n=e&&e.response&&e.response.body?`${e.response.body.code} ${e.response.body.message}`:e.message,new $e(`Could not resolve reference: ${n}`,t,e)}function Qe(e){return(e+"").split("#")}function Xe(e,t){const n=Ve[e];if(n&&!ge.isPromise(n))try{const e=tt(t,n);return Object.assign(Promise.resolve(e),{__value:e})}catch(e){return Promise.reject(e)}return et(e).then((e=>tt(t,e)))}function et(e){const t=Ve[e];return t?ge.isPromise(t)?t:Promise.resolve(t):(Ve[e]=Ke.fetchJSON(e).then((t=>(Ve[e]=t,t))),Ve[e])}function tt(e,t){const n=nt(e);if(n.length<1)return t;const r=ge.getIn(t,n);if(void 0===r)throw new $e(`Could not resolve pointer: ${e} does not exist in document`,{pointer:e});return r}function nt(e){if("string"!=typeof e)throw new TypeError("Expected a string, got a "+typeof e);return"/"===e[0]&&(e=e.substr(1)),""===e?[]:e.split("/").map(rt)}function rt(e){if("string"!=typeof e)return e;return new URLSearchParams(`=${e.replace(/~1/g,"/").replace(/~0/g,"~")}`).get("")}function ot(e){return new URLSearchParams([["",e.replace(/~/g,"~0").replace(/\//g,"~1")]]).toString().slice(1)}const at=e=>!e||"/"===e||"#"===e;function it(e,t){if(at(t))return!0;const n=e.charAt(t.length),r=t.slice(-1);return 0===e.indexOf(t)&&(!n||"/"===n||"#"===n)&&"#"!==r}const st={key:"allOf",plugin:(e,t,n,r,o)=>{if(o.meta&&o.meta.$$ref)return;const a=n.slice(0,-1);if(Ue(a))return;if(!Array.isArray(e)){const e=new TypeError("allOf must be an array");return e.fullPath=n,e}let i=!1,l=o.value;if(a.forEach((e=>{l&&(l=l[e])})),l=s()({},l),0===Object.keys(l).length)return;delete l.allOf;const u=[];return u.push(r.replace(a,{})),e.forEach(((e,t)=>{if(!r.isObject(e)){if(i)return null;i=!0;const e=new TypeError("Elements in allOf must be objects");return e.fullPath=n,u.push(e)}u.push(r.mergeDeep(a,e));const o=function(e,t){let{specmap:n,getBaseUrlForNodePath:r=(e=>n.getContext([...t,...e]).baseDoc),targetKeys:o=["$ref","$$ref"]}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const a=[];return Me()(e).forEach((function(){if(o.includes(this.key)&&"string"==typeof this.node){const e=this.path,o=t.concat(this.path),i=ze(this.node,r(e));a.push(n.replace(o,i))}})),a}(e,n.slice(0,-1),{getBaseUrlForNodePath:e=>r.getContext([...n,t,...e]).baseDoc,specmap:r});u.push(...o)})),l.example&&u.push(r.remove([].concat(a,"example"))),u.push(r.mergeDeep(a,l)),l.$$ref||u.push(r.remove([].concat(a,"$$ref"))),u}},lt={key:"parameters",plugin:(e,t,n,r)=>{if(Array.isArray(e)&&e.length){const t=Object.assign([],e),o=n.slice(0,-1),a=s()({},ge.getIn(r.spec,o));for(let o=0;o{const o=s()({},e);for(const t in e)try{o[t].default=r.modelPropertyMacro(o[t])}catch(e){const t=new Error(e);return t.fullPath=n,t}return ge.replace(n,o)}};class ct{constructor(e){this.root=pt(e||{})}set(e,t){const n=this.getParent(e,!0);if(!n)return void ft(this.root,t,null);const r=e[e.length-1],{children:o}=n;o[r]?ft(o[r],t,n):o[r]=pt(t,n)}get(e){if((e=e||[]).length<1)return this.root.value;let t,n,r=this.root;for(let o=0;o{if(!e)return e;const{children:r}=e;return!r[n]&&t&&(r[n]=pt(null,e)),r[n]}),this.root)}}function pt(e,t){return ft({children:{}},e,t)}function ft(e,t,n){return e.value=t||{},e.protoValue=n?s()(s()({},n.protoValue),e.value):e.value,Object.keys(e.children).forEach((t=>{const n=e.children[t];e.children[t]=ft(n,n.value,e)})),e}const ht=()=>{};class dt{static getPluginName(e){return e.pluginName}static getPatchesOfType(e,t){return e.filter(t)}constructor(e){Object.assign(this,{spec:"",debugLevel:"info",plugins:[],pluginHistory:{},errors:[],mutations:[],promisedPatches:[],state:{},patches:[],context:{},contextTree:new ct,showDebug:!1,allPatches:[],pluginProp:"specMap",libMethods:Object.assign(Object.create(this),ge,{getInstance:()=>this}),allowMetaPatches:!1},e),this.get=this._get.bind(this),this.getContext=this._getContext.bind(this),this.hasRun=this._hasRun.bind(this),this.wrappedPlugins=this.plugins.map(this.wrapPlugin.bind(this)).filter(ge.isFunction),this.patches.push(ge.add([],this.spec)),this.patches.push(ge.context([],this.context)),this.updatePatches(this.patches)}debug(e){if(this.debugLevel===e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r1?t-1:0),r=1;r!Array.isArray(e)||e.every(((e,n)=>e===t[n]));return function*(r,o){const a={};for(const e of r.filter(ge.isAdditiveMutation))yield*i(e.value,e.path,e);function*i(r,s,l){if(ge.isObject(r)){const u=s.length-1,c=s[u],p=s.indexOf("properties"),f="properties"===c&&u===p,h=o.allowMetaPatches&&a[r.$$ref];for(const u of Object.keys(r)){const c=r[u],p=s.concat(u),d=ge.isObject(c),m=r.$$ref;if(h||d&&(o.allowMetaPatches&&m&&(a[m]=!0),yield*i(c,p,l)),!f&&u===e.key){const r=t(n,s);n&&!r||(yield e.plugin(c,u,p,o,l))}}}else e.key===s[s.length-1]&&(yield e.plugin(r,e.key,s,o))}}}(e)),Object.assign(r.bind(o),{pluginName:e.name||t,isGenerator:ge.isGenerator(r)})}nextPlugin(){return this.wrappedPlugins.find((e=>this.getMutationsForPlugin(e).length>0))}nextPromisedPatch(){if(this.promisedPatches.length>0)return Promise.race(this.promisedPatches.map((e=>e.value)))}getPluginHistory(e){const t=this.constructor.getPluginName(e);return this.pluginHistory[t]||[]}getPluginRunCount(e){return this.getPluginHistory(e).length}getPluginHistoryTip(e){const t=this.getPluginHistory(e);return t&&t[t.length-1]||{}}getPluginMutationIndex(e){const t=this.getPluginHistoryTip(e).mutationIndex;return"number"!=typeof t?-1:t}updatePluginHistory(e,t){const n=this.constructor.getPluginName(e);this.pluginHistory[n]=this.pluginHistory[n]||[],this.pluginHistory[n].push(t)}updatePatches(e){ge.normalizeArray(e).forEach((e=>{if(e instanceof Error)this.errors.push(e);else try{if(!ge.isObject(e))return void this.debug("updatePatches","Got a non-object patch",e);if(this.showDebug&&this.allPatches.push(e),ge.isPromise(e.value))return this.promisedPatches.push(e),void this.promisedPatchThen(e);if(ge.isContextPatch(e))return void this.setContext(e.path,e.value);if(ge.isMutation(e))return void this.updateMutations(e)}catch(e){console.error(e),this.errors.push(e)}}))}updateMutations(e){"object"==typeof e.value&&!Array.isArray(e.value)&&this.allowMetaPatches&&(e.value=s()({},e.value));const t=ge.applyPatch(this.state,e,{allowMetaPatches:this.allowMetaPatches});t&&(this.mutations.push(e),this.state=t)}removePromisedPatch(e){const t=this.promisedPatches.indexOf(e);t<0?this.debug("Tried to remove a promisedPatch that isn't there!"):this.promisedPatches.splice(t,1)}promisedPatchThen(e){return e.value=e.value.then((t=>{const n=s()(s()({},e),{},{value:t});this.removePromisedPatch(e),this.updatePatches(n)})).catch((t=>{this.removePromisedPatch(e),this.updatePatches(t)})),e.value}getMutations(e,t){return e=e||0,"number"!=typeof t&&(t=this.mutations.length),this.mutations.slice(e,t)}getCurrentMutations(){return this.getMutationsForPlugin(this.getCurrentPlugin())}getMutationsForPlugin(e){const t=this.getPluginMutationIndex(e);return this.getMutations(t+1)}getCurrentPlugin(){return this.currentPlugin}getLib(){return this.libMethods}_get(e){return ge.getIn(this.state,e)}_getContext(e){return this.contextTree.get(e)}setContext(e,t){return this.contextTree.set(e,t)}_hasRun(e){return this.getPluginRunCount(this.getCurrentPlugin())>(e||0)}dispatch(){const e=this,t=this.nextPlugin();if(!t){const e=this.nextPromisedPatch();if(e)return e.then((()=>this.dispatch())).catch((()=>this.dispatch()));const t={spec:this.state,errors:this.errors};return this.showDebug&&(t.patches=this.allPatches),Promise.resolve(t)}if(e.pluginCount=e.pluginCount||{},e.pluginCount[t]=(e.pluginCount[t]||0)+1,e.pluginCount[t]>100)return Promise.resolve({spec:e.state,errors:e.errors.concat(new Error("We've reached a hard limit of 100 plugin runs"))});if(t!==this.currentPlugin&&this.promisedPatches.length){const e=this.promisedPatches.map((e=>e.value));return Promise.all(e.map((e=>e.then(ht,ht)))).then((()=>this.dispatch()))}return function(){e.currentPlugin=t;const r=e.getCurrentMutations(),o=e.mutations.length-1;try{if(t.isGenerator)for(const o of t(r,e.getLib()))n(o);else{n(t(r,e.getLib()))}}catch(e){console.error(e),n([Object.assign(Object.create(e),{plugin:t})])}finally{e.updatePluginHistory(t,{mutationIndex:o})}return e.dispatch()}();function n(n){n&&(n=ge.fullyNormalizeArray(n),e.updatePatches(n,t))}}}const mt={refs:Ge,allOf:st,parameters:lt,properties:ut};var gt=n(32454);function yt(e){const{spec:t}=e,{paths:n}=t,r={};if(!n||t.$$normalized)return e;for(const e in n){const o=n[e];if(null==o||!["object","function"].includes(typeof o))continue;const a=o.parameters;for(const n in o){const i=o[n];if(null==i||!["object","function"].includes(typeof i))continue;const s=(0,gt.Z)(i,e,n);if(s){r[s]?r[s].push(i):r[s]=[i];const e=r[s];if(e.length>1)e.forEach(((e,t)=>{e.__originalOperationId=e.__originalOperationId||e.operationId,e.operationId=`${s}${t+1}`}));else if(void 0!==i.operationId){const t=e[0];t.__originalOperationId=t.__originalOperationId||i.operationId,t.operationId=s}}if("parameters"!==n){const e=[],n={};for(const r in t)"produces"!==r&&"consumes"!==r&&"security"!==r||(n[r]=t[r],e.push(n));if(a&&(n.parameters=a,e.push(n)),e.length)for(const t of e)for(const e in t)if(i[e]){if("parameters"===e)for(const n of t[e]){i[e].some((e=>e.name&&e.name===n.name||e.$ref&&e.$ref===n.$ref||e.$$ref&&e.$$ref===n.$$ref||e===n))||i[e].push(n)}}else i[e]=t[e]}}}return t.$$normalized=!0,e}async function vt(e){const{spec:t,mode:n,allowMetaPatches:r=!0,pathDiscriminator:o,modelPropertyMacro:a,parameterMacro:i,requestInterceptor:s,responseInterceptor:l,skipNormalization:c,useCircularStructures:p}=e,f=M(e),h=D(e);return function(e){f&&(mt.refs.docCache[f]=e);mt.refs.fetchJSON=u(h,{requestInterceptor:s,responseInterceptor:l});const t=[mt.refs];"function"==typeof i&&t.push(mt.parameters);"function"==typeof a&&t.push(mt.properties);"strict"!==n&&t.push(mt.allOf);return(d={spec:e,context:{baseDoc:f},plugins:t,allowMetaPatches:r,pathDiscriminator:o,parameterMacro:i,modelPropertyMacro:a,useCircularStructures:p},new dt(d).dispatch()).then(c?async e=>e:yt);var d}(t)}const bt={name:"generic",match:()=>!0,normalize(e){let{spec:t}=e;const{spec:n}=yt({spec:t});return n},resolve:async e=>vt(e)};const wt=e=>{try{const{openapi:t}=e;return"string"==typeof t&&t.startsWith("3.0")}catch{return!1}},Et=e=>wt(e)||(e=>{try{const{openapi:t}=e;return"string"==typeof t&&t.startsWith("3.1")}catch{return!1}})(e),xt={name:"openapi-2",match(e){let{spec:t}=e;return(e=>{try{const{swagger:t}=e;return"2.0"===t}catch{return!1}})(t)},normalize(e){let{spec:t}=e;const{spec:n}=yt({spec:t});return n},resolve:async e=>async function(e){return vt(e)}(e)};const _t={name:"openapi-3-0",match(e){let{spec:t}=e;return wt(t)},normalize(e){let{spec:t}=e;const{spec:n}=yt({spec:t});return n},resolve:async e=>async function(e){return vt(e)}(e)},St=(At={strategies:[_t,xt,bt]},async e=>(async e=>{const{spec:t,requestInterceptor:n,responseInterceptor:r}=e,o=M(e),a=D(e),i=t||await u(a,{requestInterceptor:n,responseInterceptor:r})(o),l=s()(s()({},e),{},{spec:i});return e.strategies.find((e=>e.match(l))).resolve(l)})(s()(s()({},At),e)));var At,Ct=n(88436),kt=n.n(Ct),Ot=n(27361),jt=n.n(Ot),Tt=n(76489);function It(e){return"[object Object]"===Object.prototype.toString.call(e)}function Nt(e){var t,n;return!1!==It(e)&&(void 0===(t=e.constructor)||!1!==It(n=t.prototype)&&!1!==n.hasOwnProperty("isPrototypeOf"))}const Pt={body:function(e){let{req:t,value:n}=e;t.body=n},header:function(e){let{req:t,parameter:n,value:r}=e;t.headers=t.headers||{},void 0!==r&&(t.headers[n.name]=r)},query:function(e){let{req:t,value:n,parameter:r}=e;t.query=t.query||{},!1===n&&"boolean"===r.type&&(n="false");0===n&&["number","integer"].indexOf(r.type)>-1&&(n="0");if(n)t.query[r.name]={collectionFormat:r.collectionFormat,value:n};else if(r.allowEmptyValue&&void 0!==n){const e=r.name;t.query[e]=t.query[e]||{},t.query[e].allowEmptyValue=!0}},path:function(e){let{req:t,value:n,parameter:r}=e;t.url=t.url.split(`{${r.name}}`).join(encodeURIComponent(n))},formData:function(e){let{req:t,value:n,parameter:r}=e;(n||r.allowEmptyValue)&&(t.form=t.form||{},t.form[r.name]={value:n,allowEmptyValue:r.allowEmptyValue,collectionFormat:r.collectionFormat})}};function Rt(e,t){return t.includes("application/json")?"string"==typeof e?e:JSON.stringify(e):e.toString()}function Mt(e){let{req:t,value:n,parameter:r}=e;const{name:o,style:a,explode:i,content:s}=r;if(s){const e=Object.keys(s)[0];return void(t.url=t.url.split(`{${o}}`).join(b(Rt(n,e),{escape:!0})))}const l=w({key:r.name,value:n,style:a||"simple",explode:i||!1,escape:!0});t.url=t.url.split(`{${o}}`).join(l)}function Dt(e){let{req:t,value:n,parameter:r}=e;if(t.query=t.query||{},r.content){const e=Object.keys(r.content)[0];t.query[r.name]=Rt(n,e)}else if(!1===n&&(n="false"),0===n&&(n="0"),n){const{style:e,explode:o,allowReserved:a}=r;t.query[r.name]={value:n,serializationOption:{style:e,explode:o,allowReserved:a}}}else if(r.allowEmptyValue&&void 0!==n){const e=r.name;t.query[e]=t.query[e]||{},t.query[e].allowEmptyValue=!0}}const Lt=["accept","authorization","content-type"];function Bt(e){let{req:t,parameter:n,value:r}=e;if(t.headers=t.headers||{},!(Lt.indexOf(n.name.toLowerCase())>-1))if(n.content){const e=Object.keys(n.content)[0];t.headers[n.name]=Rt(r,e)}else void 0!==r&&(t.headers[n.name]=w({key:n.name,value:r,style:n.style||"simple",explode:void 0!==n.explode&&n.explode,escape:!1}))}function Ft(e){let{req:t,parameter:n,value:r}=e;t.headers=t.headers||{};const o=typeof r;if(n.content){const e=Object.keys(n.content)[0];t.headers.Cookie=`${n.name}=${Rt(r,e)}`}else if("undefined"!==o){const e="object"===o&&!Array.isArray(r)&&n.explode?"":`${n.name}=`;t.headers.Cookie=e+w({key:n.name,value:r,escape:!1,style:n.style||"form",explode:void 0!==n.explode&&n.explode})}}const Ut="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:window,{btoa:zt}=Ut,qt=zt;function $t(e,t){const{operation:n,requestBody:r,securities:o,spec:a,attachContentTypeForEmptyPayload:i}=e;let{requestContentType:l}=e;t=function(e){let{request:t,securities:n={},operation:r={},spec:o}=e;const a=s()({},t),{authorized:i={}}=n,l=r.security||o.security||[],u=i&&!!Object.keys(i).length,c=jt()(o,["components","securitySchemes"])||{};if(a.headers=a.headers||{},a.query=a.query||{},!Object.keys(n).length||!u||!l||Array.isArray(r.security)&&!r.security.length)return t;return l.forEach((e=>{Object.keys(e).forEach((e=>{const t=i[e],n=c[e];if(!t)return;const r=t.value||t,{type:o}=n;if(t)if("apiKey"===o)"query"===n.in&&(a.query[n.name]=r),"header"===n.in&&(a.headers[n.name]=r),"cookie"===n.in&&(a.cookies[n.name]=r);else if("http"===o){if(/^basic$/i.test(n.scheme)){const e=r.username||"",t=r.password||"",n=qt(`${e}:${t}`);a.headers.Authorization=`Basic ${n}`}/^bearer$/i.test(n.scheme)&&(a.headers.Authorization=`Bearer ${r}`)}else if("oauth2"===o||"openIdConnect"===o){const e=t.token||{},r=e[n["x-tokenName"]||"access_token"];let o=e.token_type;o&&"bearer"!==o.toLowerCase()||(o="Bearer"),a.headers.Authorization=`${o} ${r}`}}))})),a}({request:t,securities:o,operation:n,spec:a});const u=n.requestBody||{},c=Object.keys(u.content||{}),p=l&&c.indexOf(l)>-1;if(r||i){if(l&&p)t.headers["Content-Type"]=l;else if(!l){const e=c[0];e&&(t.headers["Content-Type"]=e,l=e)}}else l&&p&&(t.headers["Content-Type"]=l);if(!e.responseContentType&&n.responses){const e=Object.entries(n.responses).filter((e=>{let[t,n]=e;const r=parseInt(t,10);return r>=200&&r<300&&Nt(n.content)})).reduce(((e,t)=>{let[,n]=t;return e.concat(Object.keys(n.content))}),[]);e.length>0&&(t.headers.accept=e.join(", "))}if(r)if(l){if(c.indexOf(l)>-1)if("application/x-www-form-urlencoded"===l||"multipart/form-data"===l)if("object"==typeof r){const e=(u.content[l]||{}).encoding||{};t.form={},Object.keys(r).forEach((n=>{t.form[n]={value:r[n],encoding:e[n]||{}}}))}else t.form=r;else t.body=r}else t.body=r;return t}function Vt(e,t){const{spec:n,operation:r,securities:o,requestContentType:a,responseContentType:i,attachContentTypeForEmptyPayload:l}=e;if(t=function(e){let{request:t,securities:n={},operation:r={},spec:o}=e;const a=s()({},t),{authorized:i={},specSecurity:l=[]}=n,u=r.security||l,c=i&&!!Object.keys(i).length,p=o.securityDefinitions;if(a.headers=a.headers||{},a.query=a.query||{},!Object.keys(n).length||!c||!u||Array.isArray(r.security)&&!r.security.length)return t;return u.forEach((e=>{Object.keys(e).forEach((e=>{const t=i[e];if(!t)return;const{token:n}=t,r=t.value||t,o=p[e],{type:s}=o,l=o["x-tokenName"]||"access_token",u=n&&n[l];let c=n&&n.token_type;if(t)if("apiKey"===s){const e="query"===o.in?"query":"headers";a[e]=a[e]||{},a[e][o.name]=r}else if("basic"===s)if(r.header)a.headers.authorization=r.header;else{const e=r.username||"",t=r.password||"";r.base64=qt(`${e}:${t}`),a.headers.authorization=`Basic ${r.base64}`}else"oauth2"===s&&u&&(c=c&&"bearer"!==c.toLowerCase()?c:"Bearer",a.headers.authorization=`${c} ${u}`)}))})),a}({request:t,securities:o,operation:r,spec:n}),t.body||t.form||l)a?t.headers["Content-Type"]=a:Array.isArray(r.consumes)?[t.headers["Content-Type"]]=r.consumes:Array.isArray(n.consumes)?[t.headers["Content-Type"]]=n.consumes:r.parameters&&r.parameters.filter((e=>"file"===e.type)).length?t.headers["Content-Type"]="multipart/form-data":r.parameters&&r.parameters.filter((e=>"formData"===e.in)).length&&(t.headers["Content-Type"]="application/x-www-form-urlencoded");else if(a){const e=r.parameters&&r.parameters.filter((e=>"body"===e.in)).length>0,n=r.parameters&&r.parameters.filter((e=>"formData"===e.in)).length>0;(e||n)&&(t.headers["Content-Type"]=a)}return!i&&Array.isArray(r.produces)&&r.produces.length>0&&(t.headers.accept=r.produces.join(", ")),t}function Ht(e,t){return`${t.toLowerCase()}-${e}`}const Wt=["http","fetch","spec","operationId","pathName","method","parameters","securities"],Jt=e=>Array.isArray(e)?e:[],Kt=Pe("OperationNotFoundError",(function(e,t,n){this.originalError=n,Object.assign(this,t||{})})),Gt=(e,t)=>t.filter((t=>t.name===e)),Zt=e=>{const t={};e.forEach((e=>{t[e.in]||(t[e.in]={}),t[e.in][e.name]=e}));const n=[];return Object.keys(t).forEach((e=>{Object.keys(t[e]).forEach((r=>{n.push(t[e][r])}))})),n},Yt={buildRequest:Xt};function Qt(e){let{http:t,fetch:n,spec:r,operationId:o,pathName:a,method:i,parameters:l,securities:u}=e,c=kt()(e,Wt);const p=t||n||_;a&&i&&!o&&(o=Ht(a,i));const f=Yt.buildRequest(s()({spec:r,operationId:o,parameters:l,securities:u,http:p},c));return f.body&&(Nt(f.body)||Array.isArray(f.body))&&(f.body=JSON.stringify(f.body)),p(f)}function Xt(e){const{spec:t,operationId:n,responseContentType:r,scheme:o,requestInterceptor:i,responseInterceptor:l,contextUrl:u,userFetch:c,server:p,serverVariables:f,http:h,signal:d}=e;let{parameters:m,parameterBuilders:g}=e;const y=Et(t);g||(g=y?a:Pt);let v={url:"",credentials:h&&h.withCredentials?"include":"same-origin",headers:{},cookies:{}};d&&(v.signal=d),i&&(v.requestInterceptor=i),l&&(v.responseInterceptor=l),c&&(v.userFetch=c);const b=function(e,t){return e&&e.paths?function(e,t){return function(e,t,n){if(!e||"object"!=typeof e||!e.paths||"object"!=typeof e.paths)return null;const{paths:r}=e;for(const o in r)for(const a in r[o]){if("PARAMETERS"===a.toUpperCase())continue;const i=r[o][a];if(!i||"object"!=typeof i)continue;const s={spec:e,pathName:o,method:a.toUpperCase(),operation:i},l=t(s);if(n&&l)return s}}(e,t,!0)||null}(e,(e=>{let{pathName:n,method:r,operation:o}=e;if(!o||"object"!=typeof o)return!1;const a=o.operationId;return[(0,gt.Z)(o,n,r),Ht(n,r),a].some((e=>e&&e===t))})):null}(t,n);if(!b)throw new Kt(`Operation ${n} not found`);const{operation:w={},method:E,pathName:x}=b;if(v.url+=function(e){const t=Et(e.spec);return t?function(e){let{spec:t,pathName:n,method:r,server:o,contextUrl:a,serverVariables:i={}}=e;const s=jt()(t,["paths",n,(r||"").toLowerCase(),"servers"])||jt()(t,["paths",n,"servers"])||jt()(t,["servers"]);let l="",u=null;if(o&&s&&s.length){const e=s.map((e=>e.url));e.indexOf(o)>-1&&(l=o,u=s[e.indexOf(o)])}!l&&s&&s.length&&(l=s[0].url,[u]=s);if(l.indexOf("{")>-1){(function(e){const t=[],n=/{([^}]+)}/g;let r;for(;r=n.exec(e);)t.push(r[1]);return t})(l).forEach((e=>{if(u.variables&&u.variables[e]){const t=u.variables[e],n=i[e]||t.default,r=new RegExp(`{${e}}`,"g");l=l.replace(r,n)}}))}return function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";const n=e&&t?Ne.parse(Ne.resolve(t,e)):Ne.parse(e),r=Ne.parse(t),o=en(n.protocol)||en(r.protocol)||"",a=n.host||r.host,i=n.pathname||"";let s;s=o&&a?`${o}://${a+i}`:i;return"/"===s[s.length-1]?s.slice(0,-1):s}(l,a)}(e):function(e){let{spec:t,scheme:n,contextUrl:r=""}=e;const o=Ne.parse(r),a=Array.isArray(t.schemes)?t.schemes[0]:null,i=n||a||en(o.protocol)||"http",s=t.host||o.host||"",l=t.basePath||"";let u;u=i&&s?`${i}://${s+l}`:l;return"/"===u[u.length-1]?u.slice(0,-1):u}(e)}({spec:t,scheme:o,contextUrl:u,server:p,serverVariables:f,pathName:x,method:E}),!n)return delete v.cookies,v;v.url+=x,v.method=`${E}`.toUpperCase(),m=m||{};const _=t.paths[x]||{};r&&(v.headers.accept=r);const S=Zt([].concat(Jt(w.parameters)).concat(Jt(_.parameters)));S.forEach((e=>{const n=g[e.in];let r;if("body"===e.in&&e.schema&&e.schema.properties&&(r=m),r=e&&e.name&&m[e.name],void 0===r?r=e&&e.name&&m[`${e.in}.${e.name}`]:Gt(e.name,S).length>1&&console.warn(`Parameter '${e.name}' is ambiguous because the defined spec has more than one parameter with the name: '${e.name}' and the passed-in parameter values did not define an 'in' value.`),null!==r){if(void 0!==e.default&&void 0===r&&(r=e.default),void 0===r&&e.required&&!e.allowEmptyValue)throw new Error(`Required parameter ${e.name} is not provided`);if(y&&e.schema&&"object"===e.schema.type&&"string"==typeof r)try{r=JSON.parse(r)}catch(e){throw new Error("Could not parse object parameter value string as JSON")}n&&n({req:v,parameter:e,value:r,operation:w,spec:t})}}));const A=s()(s()({},e),{},{operation:w});if(v=y?$t(A,v):Vt(A,v),v.cookies&&Object.keys(v.cookies).length){const e=Object.keys(v.cookies).reduce(((e,t)=>{const n=v.cookies[t];return e+(e?"&":"")+Tt.serialize(t,n)}),"");v.headers.Cookie=e}return v.cookies&&delete v.cookies,R(v),v}const en=e=>e?e.replace(/\W/g,""):null;const tn=async function(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const{returnEntireTree:r,baseDoc:o,requestInterceptor:a,responseInterceptor:i,parameterMacro:l,modelPropertyMacro:u,useCircularStructures:c,strategies:p}=n,f={spec:e,pathDiscriminator:t,baseDoc:o,requestInterceptor:a,responseInterceptor:i,parameterMacro:l,modelPropertyMacro:u,useCircularStructures:c,strategies:p},h=p.find((e=>e.match(f))).normalize(f),d=await St(s()(s()({},f),{},{spec:h,allowMetaPatches:!0,skipNormalization:!0}));return!r&&Array.isArray(t)&&t.length&&(d.spec=jt()(d.spec,t)||null),d},nn=(e=>async function(t,n){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const o=s()(s()({},e),r);return tn(t,n,o)})({strategies:[_t,xt,bt]});var rn=n(34852);function on(e){let{configs:t,getConfigs:n}=e;return{fn:{fetch:(r=_,o=t.preFetch,a=t.postFetch,a=a||(e=>e),o=o||(e=>e),e=>("string"==typeof e&&(e={url:e}),x.mergeInQueryOrForm(e),e=o(e),a(r(e)))),buildRequest:Xt,execute:Qt,resolve:St,resolveSubtree:function(e,t,r){if(void 0===r){const e=n();r={modelPropertyMacro:e.modelPropertyMacro,parameterMacro:e.parameterMacro,requestInterceptor:e.requestInterceptor,responseInterceptor:e.responseInterceptor}}for(var o=arguments.length,a=new Array(o>3?o-3:0),i=3;i{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(90242);function o(){return{fn:{shallowEqualKeys:r.be}}}},48347:(e,t,n)=>{"use strict";n.r(t),n.d(t,{getDisplayName:()=>r});const r=e=>e.displayName||e.name||"Component"},73420:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(35627),o=n.n(r),a=n(90242),i=n(32466),s=n(48347),l=n(60314);const u=e=>{let{getComponents:t,getStore:n,getSystem:r}=e;const u=(c=(0,i.getComponent)(r,n,t),(0,a.HP)(c,(function(){for(var e=arguments.length,t=new Array(e),n=0;n(0,l.Z)(e,(function(){for(var e=arguments.length,t=new Array(e),n=0;n{"use strict";n.r(t),n.d(t,{getComponent:()=>se,render:()=>ie,withMappedContainer:()=>ae});var r=n(23101),o=n.n(r),a=n(28222),i=n.n(a),s=n(67294),l=n(73935),u=n(97779),c=s.createContext(null);var p=function(e){e()},f=function(){return p};var h={notify:function(){},get:function(){return[]}};function d(e,t){var n,r=h;function o(){i.onStateChange&&i.onStateChange()}function a(){n||(n=t?t.addNestedSub(o):e.subscribe(o),r=function(){var e=f(),t=null,n=null;return{clear:function(){t=null,n=null},notify:function(){e((function(){for(var e=t;e;)e.callback(),e=e.next}))},get:function(){for(var e=[],n=t;n;)e.push(n),n=n.next;return e},subscribe:function(e){var r=!0,o=n={callback:e,next:null,prev:n};return o.prev?o.prev.next=o:t=o,function(){r&&null!==t&&(r=!1,o.next?o.next.prev=o.prev:n=o.prev,o.prev?o.prev.next=o.next:t=o.next)}}}}())}var i={addNestedSub:function(e){return a(),r.subscribe(e)},notifyNestedSubs:function(){r.notify()},handleChangeWrapper:o,isSubscribed:function(){return Boolean(n)},trySubscribe:a,tryUnsubscribe:function(){n&&(n(),n=void 0,r.clear(),r=h)},getListeners:function(){return r}};return i}var m="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?s.useLayoutEffect:s.useEffect;const g=function(e){var t=e.store,n=e.context,r=e.children,o=(0,s.useMemo)((function(){var e=d(t);return{store:t,subscription:e}}),[t]),a=(0,s.useMemo)((function(){return t.getState()}),[t]);m((function(){var e=o.subscription;return e.onStateChange=e.notifyNestedSubs,e.trySubscribe(),a!==t.getState()&&e.notifyNestedSubs(),function(){e.tryUnsubscribe(),e.onStateChange=null}}),[o,a]);var i=n||c;return s.createElement(i.Provider,{value:o},r)};var y=n(87462),v=n(63366),b=n(8679),w=n.n(b),E=n(59864),x=["getDisplayName","methodName","renderCountProp","shouldHandleStateChanges","storeKey","withRef","forwardRef","context"],_=["reactReduxForwardedRef"],S=[],A=[null,null];function C(e,t){var n=e[1];return[t.payload,n+1]}function k(e,t,n){m((function(){return e.apply(void 0,t)}),n)}function O(e,t,n,r,o,a,i){e.current=r,t.current=o,n.current=!1,a.current&&(a.current=null,i())}function j(e,t,n,r,o,a,i,s,l,u){if(e){var c=!1,p=null,f=function(){if(!c){var e,n,f=t.getState();try{e=r(f,o.current)}catch(e){n=e,p=e}n||(p=null),e===a.current?i.current||l():(a.current=e,s.current=e,i.current=!0,u({type:"STORE_UPDATED",payload:{error:n}}))}};n.onStateChange=f,n.trySubscribe(),f();return function(){if(c=!0,n.tryUnsubscribe(),n.onStateChange=null,p)throw p}}}var T=function(){return[null,0]};function I(e,t){void 0===t&&(t={});var n=t,r=n.getDisplayName,o=void 0===r?function(e){return"ConnectAdvanced("+e+")"}:r,a=n.methodName,i=void 0===a?"connectAdvanced":a,l=n.renderCountProp,u=void 0===l?void 0:l,p=n.shouldHandleStateChanges,f=void 0===p||p,h=n.storeKey,m=void 0===h?"store":h,g=(n.withRef,n.forwardRef),b=void 0!==g&&g,I=n.context,N=void 0===I?c:I,P=(0,v.Z)(n,x),R=N;return function(t){var n=t.displayName||t.name||"Component",r=o(n),a=(0,y.Z)({},P,{getDisplayName:o,methodName:i,renderCountProp:u,shouldHandleStateChanges:f,storeKey:m,displayName:r,wrappedComponentName:n,WrappedComponent:t}),l=P.pure;var c=l?s.useMemo:function(e){return e()};function p(n){var r=(0,s.useMemo)((function(){var e=n.reactReduxForwardedRef,t=(0,v.Z)(n,_);return[n.context,e,t]}),[n]),o=r[0],i=r[1],l=r[2],u=(0,s.useMemo)((function(){return o&&o.Consumer&&(0,E.isContextConsumer)(s.createElement(o.Consumer,null))?o:R}),[o,R]),p=(0,s.useContext)(u),h=Boolean(n.store)&&Boolean(n.store.getState)&&Boolean(n.store.dispatch);Boolean(p)&&Boolean(p.store);var m=h?n.store:p.store,g=(0,s.useMemo)((function(){return function(t){return e(t.dispatch,a)}(m)}),[m]),b=(0,s.useMemo)((function(){if(!f)return A;var e=d(m,h?null:p.subscription),t=e.notifyNestedSubs.bind(e);return[e,t]}),[m,h,p]),w=b[0],x=b[1],I=(0,s.useMemo)((function(){return h?p:(0,y.Z)({},p,{subscription:w})}),[h,p,w]),N=(0,s.useReducer)(C,S,T),P=N[0][0],M=N[1];if(P&&P.error)throw P.error;var D=(0,s.useRef)(),L=(0,s.useRef)(l),B=(0,s.useRef)(),F=(0,s.useRef)(!1),U=c((function(){return B.current&&l===L.current?B.current:g(m.getState(),l)}),[m,P,l]);k(O,[L,D,F,l,U,B,x]),k(j,[f,m,w,g,L,D,F,B,x,M],[m,w,g]);var z=(0,s.useMemo)((function(){return s.createElement(t,(0,y.Z)({},U,{ref:i}))}),[i,t,U]);return(0,s.useMemo)((function(){return f?s.createElement(u.Provider,{value:I},z):z}),[u,z,I])}var h=l?s.memo(p):p;if(h.WrappedComponent=t,h.displayName=p.displayName=r,b){var g=s.forwardRef((function(e,t){return s.createElement(h,(0,y.Z)({},e,{reactReduxForwardedRef:t}))}));return g.displayName=r,g.WrappedComponent=t,w()(g,t)}return w()(h,t)}}function N(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}function P(e,t){if(N(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(var o=0;o=0;r--){var o=t[r](e);if(o)return o}return function(t,r){throw new Error("Invalid value of type "+typeof e+" for "+n+" argument when connecting component "+r.wrappedComponentName+".")}}function J(e,t){return e===t}function K(e){var t=void 0===e?{}:e,n=t.connectHOC,r=void 0===n?I:n,o=t.mapStateToPropsFactories,a=void 0===o?B:o,i=t.mapDispatchToPropsFactories,s=void 0===i?L:i,l=t.mergePropsFactories,u=void 0===l?U:l,c=t.selectorFactory,p=void 0===c?V:c;return function(e,t,n,o){void 0===o&&(o={});var i=o,l=i.pure,c=void 0===l||l,f=i.areStatesEqual,h=void 0===f?J:f,d=i.areOwnPropsEqual,m=void 0===d?P:d,g=i.areStatePropsEqual,b=void 0===g?P:g,w=i.areMergedPropsEqual,E=void 0===w?P:w,x=(0,v.Z)(i,H),_=W(e,a,"mapStateToProps"),S=W(t,s,"mapDispatchToProps"),A=W(n,u,"mergeProps");return r(p,(0,y.Z)({methodName:"connect",getDisplayName:function(e){return"Connect("+e+")"},shouldHandleStateChanges:Boolean(e),initMapStateToProps:_,initMapDispatchToProps:S,initMergeProps:A,pure:c,areStatesEqual:h,areOwnPropsEqual:m,areStatePropsEqual:b,areMergedPropsEqual:E},x))}}const G=K();var Z;Z=l.unstable_batchedUpdates,p=Z;var Y=n(57557),Q=n.n(Y),X=n(6557),ee=n.n(X);const te=e=>t=>{const{fn:n}=e();class r extends s.Component{render(){return s.createElement(t,o()({},e(),this.props,this.context))}}return r.displayName=`WithSystem(${n.getDisplayName(t)})`,r},ne=(e,t)=>n=>{const{fn:r}=e();class a extends s.Component{render(){return s.createElement(g,{store:t},s.createElement(n,o()({},this.props,this.context)))}}return a.displayName=`WithRoot(${r.getDisplayName(n)})`,a},re=(e,t,n)=>(0,u.qC)(n?ne(e,n):ee(),G(((n,r)=>{var o;const a={...r,...e()},i=(null===(o=t.prototype)||void 0===o?void 0:o.mapStateToProps)||(e=>({state:e}));return i(n,a)})),te(e))(t),oe=(e,t,n,r)=>{for(const o in t){const a=t[o];"function"==typeof a&&a(n[o],r[o],e())}},ae=(e,t,n)=>(t,r)=>{const{fn:o}=e(),a=n(t,"root");class l extends s.Component{constructor(t,n){super(t,n),oe(e,r,t,{})}UNSAFE_componentWillReceiveProps(t){oe(e,r,t,this.props)}render(){const e=Q()(this.props,r?i()(r):[]);return s.createElement(a,e)}}return l.displayName=`WithMappedContainer(${o.getDisplayName(a)})`,l},ie=(e,t,n,r)=>o=>{const a=n(e,t,r)("App","root");l.render(s.createElement(a,null),o)},se=(e,t,n)=>function(r,o){let a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if("string"!=typeof r)throw new TypeError("Need a string, to fetch a component. Was given a "+typeof r);const i=n(r);return i?o?"root"===o?re(e,i,t()):re(e,i):i:(a.failSilently||e().log.warn("Could not find component:",r),null)}},33424:(e,t,n)=>{"use strict";n.d(t,{d3:()=>D,C2:()=>ee});var r=n(28222),o=n.n(r),a=n(58118),i=n.n(a),s=n(63366);function l(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return function(e){if(0===e.length||1===e.length)return e;var t,n,r=e.join(".");return m[r]||(m[r]=0===(n=(t=e).length)||1===n?t:2===n?[t[0],t[1],"".concat(t[0],".").concat(t[1]),"".concat(t[1],".").concat(t[0])]:3===n?[t[0],t[1],t[2],"".concat(t[0],".").concat(t[1]),"".concat(t[0],".").concat(t[2]),"".concat(t[1],".").concat(t[0]),"".concat(t[1],".").concat(t[2]),"".concat(t[2],".").concat(t[0]),"".concat(t[2],".").concat(t[1]),"".concat(t[0],".").concat(t[1],".").concat(t[2]),"".concat(t[0],".").concat(t[2],".").concat(t[1]),"".concat(t[1],".").concat(t[0],".").concat(t[2]),"".concat(t[1],".").concat(t[2],".").concat(t[0]),"".concat(t[2],".").concat(t[0],".").concat(t[1]),"".concat(t[2],".").concat(t[1],".").concat(t[0])]:n>=4?[t[0],t[1],t[2],t[3],"".concat(t[0],".").concat(t[1]),"".concat(t[0],".").concat(t[2]),"".concat(t[0],".").concat(t[3]),"".concat(t[1],".").concat(t[0]),"".concat(t[1],".").concat(t[2]),"".concat(t[1],".").concat(t[3]),"".concat(t[2],".").concat(t[0]),"".concat(t[2],".").concat(t[1]),"".concat(t[2],".").concat(t[3]),"".concat(t[3],".").concat(t[0]),"".concat(t[3],".").concat(t[1]),"".concat(t[3],".").concat(t[2]),"".concat(t[0],".").concat(t[1],".").concat(t[2]),"".concat(t[0],".").concat(t[1],".").concat(t[3]),"".concat(t[0],".").concat(t[2],".").concat(t[1]),"".concat(t[0],".").concat(t[2],".").concat(t[3]),"".concat(t[0],".").concat(t[3],".").concat(t[1]),"".concat(t[0],".").concat(t[3],".").concat(t[2]),"".concat(t[1],".").concat(t[0],".").concat(t[2]),"".concat(t[1],".").concat(t[0],".").concat(t[3]),"".concat(t[1],".").concat(t[2],".").concat(t[0]),"".concat(t[1],".").concat(t[2],".").concat(t[3]),"".concat(t[1],".").concat(t[3],".").concat(t[0]),"".concat(t[1],".").concat(t[3],".").concat(t[2]),"".concat(t[2],".").concat(t[0],".").concat(t[1]),"".concat(t[2],".").concat(t[0],".").concat(t[3]),"".concat(t[2],".").concat(t[1],".").concat(t[0]),"".concat(t[2],".").concat(t[1],".").concat(t[3]),"".concat(t[2],".").concat(t[3],".").concat(t[0]),"".concat(t[2],".").concat(t[3],".").concat(t[1]),"".concat(t[3],".").concat(t[0],".").concat(t[1]),"".concat(t[3],".").concat(t[0],".").concat(t[2]),"".concat(t[3],".").concat(t[1],".").concat(t[0]),"".concat(t[3],".").concat(t[1],".").concat(t[2]),"".concat(t[3],".").concat(t[2],".").concat(t[0]),"".concat(t[3],".").concat(t[2],".").concat(t[1]),"".concat(t[0],".").concat(t[1],".").concat(t[2],".").concat(t[3]),"".concat(t[0],".").concat(t[1],".").concat(t[3],".").concat(t[2]),"".concat(t[0],".").concat(t[2],".").concat(t[1],".").concat(t[3]),"".concat(t[0],".").concat(t[2],".").concat(t[3],".").concat(t[1]),"".concat(t[0],".").concat(t[3],".").concat(t[1],".").concat(t[2]),"".concat(t[0],".").concat(t[3],".").concat(t[2],".").concat(t[1]),"".concat(t[1],".").concat(t[0],".").concat(t[2],".").concat(t[3]),"".concat(t[1],".").concat(t[0],".").concat(t[3],".").concat(t[2]),"".concat(t[1],".").concat(t[2],".").concat(t[0],".").concat(t[3]),"".concat(t[1],".").concat(t[2],".").concat(t[3],".").concat(t[0]),"".concat(t[1],".").concat(t[3],".").concat(t[0],".").concat(t[2]),"".concat(t[1],".").concat(t[3],".").concat(t[2],".").concat(t[0]),"".concat(t[2],".").concat(t[0],".").concat(t[1],".").concat(t[3]),"".concat(t[2],".").concat(t[0],".").concat(t[3],".").concat(t[1]),"".concat(t[2],".").concat(t[1],".").concat(t[0],".").concat(t[3]),"".concat(t[2],".").concat(t[1],".").concat(t[3],".").concat(t[0]),"".concat(t[2],".").concat(t[3],".").concat(t[0],".").concat(t[1]),"".concat(t[2],".").concat(t[3],".").concat(t[1],".").concat(t[0]),"".concat(t[3],".").concat(t[0],".").concat(t[1],".").concat(t[2]),"".concat(t[3],".").concat(t[0],".").concat(t[2],".").concat(t[1]),"".concat(t[3],".").concat(t[1],".").concat(t[0],".").concat(t[2]),"".concat(t[3],".").concat(t[1],".").concat(t[2],".").concat(t[0]),"".concat(t[3],".").concat(t[2],".").concat(t[0],".").concat(t[1]),"".concat(t[3],".").concat(t[2],".").concat(t[1],".").concat(t[0])]:void 0),m[r]}(e.filter((function(e){return"token"!==e}))).reduce((function(e,t){return d(d({},e),n[t])}),t)}function y(e){return e.join(" ")}function v(e){var t=e.node,n=e.stylesheet,r=e.style,o=void 0===r?{}:r,a=e.useInlineStyles,i=e.key,s=t.properties,l=t.type,u=t.tagName,c=t.value;if("text"===l)return c;if(u){var h,m=function(e,t){var n=0;return function(r){return n+=1,r.map((function(r,o){return v({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(o)})}))}}(n,a);if(a){var b=Object.keys(n).reduce((function(e,t){return t.split(".").forEach((function(t){e.includes(t)||e.push(t)})),e}),[]),w=s.className&&s.className.includes("token")?["token"]:[],E=s.className&&w.concat(s.className.filter((function(e){return!b.includes(e)})));h=d(d({},s),{},{className:y(E)||void 0,style:g(s.className,Object.assign({},s.style,o),n)})}else h=d(d({},s),{},{className:y(s.className)});var x=m(t.children);return p.createElement(u,(0,f.Z)({key:i},h),x)}}const b=function(e,t){return-1!==e.listLanguages().indexOf(t)};var w=["language","children","style","customStyle","codeTagProps","useInlineStyles","showLineNumbers","showInlineLineNumbers","startingLineNumber","lineNumberContainerStyle","lineNumberStyle","wrapLines","wrapLongLines","lineProps","renderer","PreTag","CodeTag","code","astGenerator"];function E(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function x(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=0;r2&&void 0!==arguments[2]?arguments[2]:[];return t||u.length>0?function(e,t){return k({children:e,lineNumber:t,lineNumberStyle:s,largestLineNumber:i,showInlineLineNumbers:o,lineProps:n,className:arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],showLineNumbers:r,wrapLongLines:l})}(e,a,u):function(e,t){if(r&&t&&o){var n=C(s,t,i);e.unshift(A(t,n))}return e}(e,a)}for(var m=function(){var e=c[h],t=e.children[0].value;if(t.match(_)){var n=t.split("\n");n.forEach((function(t,o){var i=r&&p.length+a,s={type:"text",value:"".concat(t,"\n")};if(0===o){var l=d(c.slice(f+1,h).concat(k({children:[s],className:e.properties.className})),i);p.push(l)}else if(o===n.length-1){var u=c[h+1]&&c[h+1].children&&c[h+1].children[0],m={type:"text",value:"".concat(t)};if(u){var g=k({children:[m],className:e.properties.className});c.splice(h+1,0,g)}else{var y=d([m],i,e.properties.className);p.push(y)}}else{var v=d([s],i,e.properties.className);p.push(v)}})),f=h}h++};h=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}(e,w);$=$||N;var H=d?p.createElement(S,{containerStyle:E,codeStyle:u.style||{},numberStyle:A,startingLineNumber:v,codeString:q}):null,W=o.hljs||o['pre[class*="language-"]']||{backgroundColor:"#fff"},J=I($)?"hljs":"prismjs",K=f?Object.assign({},V,{style:Object.assign({},W,i)}):Object.assign({},V,{className:V.className?"".concat(J," ").concat(V.className):J,style:Object.assign({},i)});if(u.style=x(x({},u.style),{},O?{whiteSpace:"pre-wrap"}:{whiteSpace:"pre"}),!$)return p.createElement(B,K,H,p.createElement(U,u,q));(void 0===C&&D||O)&&(C=!0),D=D||T;var G=[{type:"text",value:q}],Z=function(e){var t=e.astGenerator,n=e.language,r=e.code,o=e.defaultCodeValue;if(I(t)){var a=b(t,n);return"text"===n?{value:o,language:"text"}:a?t.highlight(n,r):t.highlightAuto(r)}try{return n&&"text"!==n?{value:t.highlight(r,n)}:{value:o}}catch(e){return{value:o}}}({astGenerator:$,language:t,code:q,defaultCodeValue:G});null===Z.language&&(Z.value=G);var Y=j(Z,C,M,d,g,v,Z.value.length+v,A,O);return p.createElement(B,K,p.createElement(U,u,!g&&H,D({rows:Y,stylesheet:o,useInlineStyles:f})))});M.registerLanguage=R.registerLanguage;const D=M;var L=n(96344);const B=n.n(L)();var F=n(82026);const U=n.n(F)();var z=n(42157);const q=n.n(z)();var $=n(61519);const V=n.n($)();var H=n(54587);const W=n.n(H)();var J=n(30786);const K=n.n(J)();var G=n(66336);const Z=n.n(G)(),Y={hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#333",color:"white"},"hljs-name":{fontWeight:"bold"},"hljs-strong":{fontWeight:"bold"},"hljs-code":{fontStyle:"italic",color:"#888"},"hljs-emphasis":{fontStyle:"italic"},"hljs-tag":{color:"#62c8f3"},"hljs-variable":{color:"#ade5fc"},"hljs-template-variable":{color:"#ade5fc"},"hljs-selector-id":{color:"#ade5fc"},"hljs-selector-class":{color:"#ade5fc"},"hljs-string":{color:"#a2fca2"},"hljs-bullet":{color:"#d36363"},"hljs-type":{color:"#ffa"},"hljs-title":{color:"#ffa"},"hljs-section":{color:"#ffa"},"hljs-attribute":{color:"#ffa"},"hljs-quote":{color:"#ffa"},"hljs-built_in":{color:"#ffa"},"hljs-builtin-name":{color:"#ffa"},"hljs-number":{color:"#d36363"},"hljs-symbol":{color:"#d36363"},"hljs-keyword":{color:"#fcc28c"},"hljs-selector-tag":{color:"#fcc28c"},"hljs-literal":{color:"#fcc28c"},"hljs-comment":{color:"#888"},"hljs-deletion":{color:"#333",backgroundColor:"#fc9b9b"},"hljs-regexp":{color:"#c6b4f0"},"hljs-link":{color:"#c6b4f0"},"hljs-meta":{color:"#fc9b9b"},"hljs-addition":{backgroundColor:"#a2fca2",color:"#333"}};D.registerLanguage("json",U),D.registerLanguage("js",B),D.registerLanguage("xml",q),D.registerLanguage("yaml",W),D.registerLanguage("http",K),D.registerLanguage("bash",V),D.registerLanguage("powershell",Z),D.registerLanguage("javascript",B);const Q={agate:Y,arta:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#222",color:"#aaa"},"hljs-subst":{color:"#aaa"},"hljs-section":{color:"#fff",fontWeight:"bold"},"hljs-comment":{color:"#444"},"hljs-quote":{color:"#444"},"hljs-meta":{color:"#444"},"hljs-string":{color:"#ffcc33"},"hljs-symbol":{color:"#ffcc33"},"hljs-bullet":{color:"#ffcc33"},"hljs-regexp":{color:"#ffcc33"},"hljs-number":{color:"#00cc66"},"hljs-addition":{color:"#00cc66"},"hljs-built_in":{color:"#32aaee"},"hljs-builtin-name":{color:"#32aaee"},"hljs-literal":{color:"#32aaee"},"hljs-type":{color:"#32aaee"},"hljs-template-variable":{color:"#32aaee"},"hljs-attribute":{color:"#32aaee"},"hljs-link":{color:"#32aaee"},"hljs-keyword":{color:"#6644aa"},"hljs-selector-tag":{color:"#6644aa"},"hljs-name":{color:"#6644aa"},"hljs-selector-id":{color:"#6644aa"},"hljs-selector-class":{color:"#6644aa"},"hljs-title":{color:"#bb1166"},"hljs-variable":{color:"#bb1166"},"hljs-deletion":{color:"#bb1166"},"hljs-template-tag":{color:"#bb1166"},"hljs-doctag":{fontWeight:"bold"},"hljs-strong":{fontWeight:"bold"},"hljs-emphasis":{fontStyle:"italic"}},monokai:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#272822",color:"#ddd"},"hljs-tag":{color:"#f92672"},"hljs-keyword":{color:"#f92672",fontWeight:"bold"},"hljs-selector-tag":{color:"#f92672",fontWeight:"bold"},"hljs-literal":{color:"#f92672",fontWeight:"bold"},"hljs-strong":{color:"#f92672"},"hljs-name":{color:"#f92672"},"hljs-code":{color:"#66d9ef"},"hljs-class .hljs-title":{color:"white"},"hljs-attribute":{color:"#bf79db"},"hljs-symbol":{color:"#bf79db"},"hljs-regexp":{color:"#bf79db"},"hljs-link":{color:"#bf79db"},"hljs-string":{color:"#a6e22e"},"hljs-bullet":{color:"#a6e22e"},"hljs-subst":{color:"#a6e22e"},"hljs-title":{color:"#a6e22e",fontWeight:"bold"},"hljs-section":{color:"#a6e22e",fontWeight:"bold"},"hljs-emphasis":{color:"#a6e22e"},"hljs-type":{color:"#a6e22e",fontWeight:"bold"},"hljs-built_in":{color:"#a6e22e"},"hljs-builtin-name":{color:"#a6e22e"},"hljs-selector-attr":{color:"#a6e22e"},"hljs-selector-pseudo":{color:"#a6e22e"},"hljs-addition":{color:"#a6e22e"},"hljs-variable":{color:"#a6e22e"},"hljs-template-tag":{color:"#a6e22e"},"hljs-template-variable":{color:"#a6e22e"},"hljs-comment":{color:"#75715e"},"hljs-quote":{color:"#75715e"},"hljs-deletion":{color:"#75715e"},"hljs-meta":{color:"#75715e"},"hljs-doctag":{fontWeight:"bold"},"hljs-selector-id":{fontWeight:"bold"}},nord:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#2E3440",color:"#D8DEE9"},"hljs-subst":{color:"#D8DEE9"},"hljs-selector-tag":{color:"#81A1C1"},"hljs-selector-id":{color:"#8FBCBB",fontWeight:"bold"},"hljs-selector-class":{color:"#8FBCBB"},"hljs-selector-attr":{color:"#8FBCBB"},"hljs-selector-pseudo":{color:"#88C0D0"},"hljs-addition":{backgroundColor:"rgba(163, 190, 140, 0.5)"},"hljs-deletion":{backgroundColor:"rgba(191, 97, 106, 0.5)"},"hljs-built_in":{color:"#8FBCBB"},"hljs-type":{color:"#8FBCBB"},"hljs-class":{color:"#8FBCBB"},"hljs-function":{color:"#88C0D0"},"hljs-function > .hljs-title":{color:"#88C0D0"},"hljs-keyword":{color:"#81A1C1"},"hljs-literal":{color:"#81A1C1"},"hljs-symbol":{color:"#81A1C1"},"hljs-number":{color:"#B48EAD"},"hljs-regexp":{color:"#EBCB8B"},"hljs-string":{color:"#A3BE8C"},"hljs-title":{color:"#8FBCBB"},"hljs-params":{color:"#D8DEE9"},"hljs-bullet":{color:"#81A1C1"},"hljs-code":{color:"#8FBCBB"},"hljs-emphasis":{fontStyle:"italic"},"hljs-formula":{color:"#8FBCBB"},"hljs-strong":{fontWeight:"bold"},"hljs-link:hover":{textDecoration:"underline"},"hljs-quote":{color:"#4C566A"},"hljs-comment":{color:"#4C566A"},"hljs-doctag":{color:"#8FBCBB"},"hljs-meta":{color:"#5E81AC"},"hljs-meta-keyword":{color:"#5E81AC"},"hljs-meta-string":{color:"#A3BE8C"},"hljs-attr":{color:"#8FBCBB"},"hljs-attribute":{color:"#D8DEE9"},"hljs-builtin-name":{color:"#81A1C1"},"hljs-name":{color:"#81A1C1"},"hljs-section":{color:"#88C0D0"},"hljs-tag":{color:"#81A1C1"},"hljs-variable":{color:"#D8DEE9"},"hljs-template-variable":{color:"#D8DEE9"},"hljs-template-tag":{color:"#5E81AC"},"abnf .hljs-attribute":{color:"#88C0D0"},"abnf .hljs-symbol":{color:"#EBCB8B"},"apache .hljs-attribute":{color:"#88C0D0"},"apache .hljs-section":{color:"#81A1C1"},"arduino .hljs-built_in":{color:"#88C0D0"},"aspectj .hljs-meta":{color:"#D08770"},"aspectj > .hljs-title":{color:"#88C0D0"},"bnf .hljs-attribute":{color:"#8FBCBB"},"clojure .hljs-name":{color:"#88C0D0"},"clojure .hljs-symbol":{color:"#EBCB8B"},"coq .hljs-built_in":{color:"#88C0D0"},"cpp .hljs-meta-string":{color:"#8FBCBB"},"css .hljs-built_in":{color:"#88C0D0"},"css .hljs-keyword":{color:"#D08770"},"diff .hljs-meta":{color:"#8FBCBB"},"ebnf .hljs-attribute":{color:"#8FBCBB"},"glsl .hljs-built_in":{color:"#88C0D0"},"groovy .hljs-meta:not(:first-child)":{color:"#D08770"},"haxe .hljs-meta":{color:"#D08770"},"java .hljs-meta":{color:"#D08770"},"ldif .hljs-attribute":{color:"#8FBCBB"},"lisp .hljs-name":{color:"#88C0D0"},"lua .hljs-built_in":{color:"#88C0D0"},"moonscript .hljs-built_in":{color:"#88C0D0"},"nginx .hljs-attribute":{color:"#88C0D0"},"nginx .hljs-section":{color:"#5E81AC"},"pf .hljs-built_in":{color:"#88C0D0"},"processing .hljs-built_in":{color:"#88C0D0"},"scss .hljs-keyword":{color:"#81A1C1"},"stylus .hljs-keyword":{color:"#81A1C1"},"swift .hljs-meta":{color:"#D08770"},"vim .hljs-built_in":{color:"#88C0D0",fontStyle:"italic"},"yaml .hljs-meta":{color:"#D08770"}},obsidian:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#282b2e",color:"#e0e2e4"},"hljs-keyword":{color:"#93c763",fontWeight:"bold"},"hljs-selector-tag":{color:"#93c763",fontWeight:"bold"},"hljs-literal":{color:"#93c763",fontWeight:"bold"},"hljs-selector-id":{color:"#93c763"},"hljs-number":{color:"#ffcd22"},"hljs-attribute":{color:"#668bb0"},"hljs-code":{color:"white"},"hljs-class .hljs-title":{color:"white"},"hljs-section":{color:"white",fontWeight:"bold"},"hljs-regexp":{color:"#d39745"},"hljs-link":{color:"#d39745"},"hljs-meta":{color:"#557182"},"hljs-tag":{color:"#8cbbad"},"hljs-name":{color:"#8cbbad",fontWeight:"bold"},"hljs-bullet":{color:"#8cbbad"},"hljs-subst":{color:"#8cbbad"},"hljs-emphasis":{color:"#8cbbad"},"hljs-type":{color:"#8cbbad",fontWeight:"bold"},"hljs-built_in":{color:"#8cbbad"},"hljs-selector-attr":{color:"#8cbbad"},"hljs-selector-pseudo":{color:"#8cbbad"},"hljs-addition":{color:"#8cbbad"},"hljs-variable":{color:"#8cbbad"},"hljs-template-tag":{color:"#8cbbad"},"hljs-template-variable":{color:"#8cbbad"},"hljs-string":{color:"#ec7600"},"hljs-symbol":{color:"#ec7600"},"hljs-comment":{color:"#818e96"},"hljs-quote":{color:"#818e96"},"hljs-deletion":{color:"#818e96"},"hljs-selector-class":{color:"#A082BD"},"hljs-doctag":{fontWeight:"bold"},"hljs-title":{fontWeight:"bold"},"hljs-strong":{fontWeight:"bold"}},"tomorrow-night":{"hljs-comment":{color:"#969896"},"hljs-quote":{color:"#969896"},"hljs-variable":{color:"#cc6666"},"hljs-template-variable":{color:"#cc6666"},"hljs-tag":{color:"#cc6666"},"hljs-name":{color:"#cc6666"},"hljs-selector-id":{color:"#cc6666"},"hljs-selector-class":{color:"#cc6666"},"hljs-regexp":{color:"#cc6666"},"hljs-deletion":{color:"#cc6666"},"hljs-number":{color:"#de935f"},"hljs-built_in":{color:"#de935f"},"hljs-builtin-name":{color:"#de935f"},"hljs-literal":{color:"#de935f"},"hljs-type":{color:"#de935f"},"hljs-params":{color:"#de935f"},"hljs-meta":{color:"#de935f"},"hljs-link":{color:"#de935f"},"hljs-attribute":{color:"#f0c674"},"hljs-string":{color:"#b5bd68"},"hljs-symbol":{color:"#b5bd68"},"hljs-bullet":{color:"#b5bd68"},"hljs-addition":{color:"#b5bd68"},"hljs-title":{color:"#81a2be"},"hljs-section":{color:"#81a2be"},"hljs-keyword":{color:"#b294bb"},"hljs-selector-tag":{color:"#b294bb"},hljs:{display:"block",overflowX:"auto",background:"#1d1f21",color:"#c5c8c6",padding:"0.5em"},"hljs-emphasis":{fontStyle:"italic"},"hljs-strong":{fontWeight:"bold"}}},X=o()(Q),ee=e=>i()(X).call(X,e)?Q[e]:(console.warn(`Request style '${e}' is not available, returning default instead`),Y)},90242:(e,t,n)=>{"use strict";n.d(t,{AF:()=>he,Ay:()=>be,D$:()=>ut,DR:()=>Se,GZ:()=>Qe,HP:()=>ve,Ik:()=>qe,J6:()=>ot,Kn:()=>me,LQ:()=>de,Nm:()=>et,O2:()=>mt,Pz:()=>lt,Q2:()=>we,QG:()=>nt,UG:()=>Ge,Uj:()=>ft,V9:()=>ct,Wl:()=>ge,XV:()=>st,Xb:()=>ht,Zl:()=>Ae,_5:()=>Ee,be:()=>Xe,cz:()=>pt,gp:()=>_e,hW:()=>tt,iQ:()=>xe,kJ:()=>ye,mz:()=>pe,nX:()=>at,oG:()=>fe,oJ:()=>rt,po:()=>it,r3:()=>Ze,wh:()=>Ye,xi:()=>Ke});var r=n(58309),o=n.n(r),a=n(97606),i=n.n(a),s=n(74386),l=n.n(s),u=n(86),c=n.n(u),p=n(14418),f=n.n(p),h=n(28222),d=n.n(h),m=(n(11189),n(24282)),g=n.n(m),y=n(76986),v=n.n(y),b=n(2578),w=n.n(b),E=n(24278),x=n.n(E),_=(n(39022),n(92039)),S=n.n(_),A=(n(58118),n(35627)),C=n.n(A),k=n(11882),O=n.n(k),j=n(51679),T=n.n(j),I=n(27043),N=n.n(I),P=n(81607),R=n.n(P),M=n(43393),D=n.n(M),L=n(17967),B=n(68929),F=n.n(B),U=n(11700),z=n.n(U),q=n(88306),$=n.n(q),V=n(13311),H=n.n(V),W=n(59704),J=n.n(W),K=n(77813),G=n.n(K),Z=n(23560),Y=n.n(Z),Q=n(57050),X=n(27504),ee=n(8269),te=n.n(ee),ne=n(19069),re=n(92282),oe=n.n(re),ae=n(89072),ie=n.n(ae),se=n(1272),le=n(48764).Buffer;const ue="default",ce=e=>D().Iterable.isIterable(e);function pe(e){return me(e)?ce(e)?e.toJS():e:{}}function fe(e){var t,n;if(ce(e))return e;if(e instanceof X.Z.File)return e;if(!me(e))return e;if(o()(e))return i()(n=D().Seq(e)).call(n,fe).toList();if(Y()(l()(e))){var r;const t=function(e){if(!Y()(l()(e)))return e;const t={},n="_**[]",r={};for(let o of l()(e).call(e))if(t[o[0]]||r[o[0]]&&r[o[0]].containsMultiple){if(!r[o[0]]){r[o[0]]={containsMultiple:!0,length:1},t[`${o[0]}${n}${r[o[0]].length}`]=t[o[0]],delete t[o[0]]}r[o[0]].length+=1,t[`${o[0]}${n}${r[o[0]].length}`]=o[1]}else t[o[0]]=o[1];return t}(e);return i()(r=D().OrderedMap(t)).call(r,fe)}return i()(t=D().OrderedMap(e)).call(t,fe)}function he(e){return o()(e)?e:[e]}function de(e){return"function"==typeof e}function me(e){return!!e&&"object"==typeof e}function ge(e){return"function"==typeof e}function ye(e){return o()(e)}const ve=$();function be(e,t){var n;return g()(n=d()(e)).call(n,((n,r)=>(n[r]=t(e[r],r),n)),{})}function we(e,t){var n;return g()(n=d()(e)).call(n,((n,r)=>{let o=t(e[r],r);return o&&"object"==typeof o&&v()(n,o),n}),{})}function Ee(e){return t=>{let{dispatch:n,getState:r}=t;return t=>n=>"function"==typeof n?n(e()):t(n)}}function xe(e){var t;let n=e.keySeq();return n.contains(ue)?ue:w()(t=f()(n).call(n,(e=>"2"===(e+"")[0]))).call(t).first()}function _e(e,t){if(!D().Iterable.isIterable(e))return D().List();let n=e.getIn(o()(t)?t:[t]);return D().List.isList(n)?n:D().List()}function Se(e){let t,n=[/filename\*=[^']+'\w*'"([^"]+)";?/i,/filename\*=[^']+'\w*'([^;]+);?/i,/filename="([^;]*);?"/i,/filename=([^;]*);?/i];if(S()(n).call(n,(n=>(t=n.exec(e),null!==t))),null!==t&&t.length>1)try{return decodeURIComponent(t[1])}catch(e){console.error(e)}return null}function Ae(e){return t=e.replace(/\.[^./]*$/,""),z()(F()(t));var t}const Ce=(e,t)=>{if(e>t)return`Value must be less than ${t}`},ke=(e,t)=>{if(e{if(!/^-?\d+(\.?\d+)?$/.test(e))return"Value must be a number"},je=e=>{if(!/^-?\d+$/.test(e))return"Value must be an integer"},Te=e=>{if(e&&!(e instanceof X.Z.File))return"Value must be a file"},Ie=e=>{if("true"!==e&&"false"!==e&&!0!==e&&!1!==e)return"Value must be a boolean"},Ne=e=>{if(e&&"string"!=typeof e)return"Value must be a string"},Pe=e=>{if(isNaN(Date.parse(e)))return"Value must be a DateTime"},Re=e=>{if(e=e.toString().toLowerCase(),!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[)}]?$/.test(e))return"Value must be a Guid"},Me=(e,t)=>{if(e.length>t)return`Value must be no longer than ${t} character${1!==t?"s":""}`},De=(e,t)=>{if(e&&("true"===t||!0===t)){const t=(0,M.fromJS)(e),n=t.toSet();if(e.length>n.size){let e=(0,M.Set)();if(c()(t).call(t,((n,r)=>{f()(t).call(t,(e=>ge(e.equals)?e.equals(n):e===n)).size>1&&(e=e.add(r))})),0!==e.size)return i()(e).call(e,(e=>({index:e,error:"No duplicates allowed."}))).toArray()}}},Le=(e,t)=>{if(!e&&t>=1||e&&e.length{if(e&&e.length>t)return`Array must not contain more then ${t} item${1===t?"":"s"}`},Fe=(e,t)=>{if(e.length{if(!new RegExp(t).test(e))return"Value must follow pattern "+t};function ze(e,t,n,r,a){if(!t)return[];let s=[],l=t.get("nullable"),u=t.get("required"),p=t.get("maximum"),f=t.get("minimum"),h=t.get("type"),d=t.get("format"),m=t.get("maxLength"),g=t.get("minLength"),y=t.get("uniqueItems"),v=t.get("maxItems"),b=t.get("minItems"),w=t.get("pattern");const E=n||!0===u,x=null!=e;if(l&&null===e||!h||!(E||x&&"array"===h||!(!E&&!x)))return[];let _="string"===h&&e,A="array"===h&&o()(e)&&e.length,C="array"===h&&D().List.isList(e)&&e.count();const k=[_,A,C,"array"===h&&"string"==typeof e&&e,"file"===h&&e instanceof X.Z.File,"boolean"===h&&(e||!1===e),"number"===h&&(e||0===e),"integer"===h&&(e||0===e),"object"===h&&"object"==typeof e&&null!==e,"object"===h&&"string"==typeof e&&e],O=S()(k).call(k,(e=>!!e));if(E&&!O&&!r)return s.push("Required field is not provided"),s;if("object"===h&&(null===a||"application/json"===a)){let n=e;if("string"==typeof e)try{n=JSON.parse(e)}catch(e){return s.push("Parameter string value must be valid JSON"),s}var j;if(t&&t.has("required")&&ge(u.isList)&&u.isList()&&c()(u).call(u,(e=>{void 0===n[e]&&s.push({propKey:e,error:"Required property not found"})})),t&&t.has("properties"))c()(j=t.get("properties")).call(j,((e,t)=>{const o=ze(n[t],e,!1,r,a);s.push(...i()(o).call(o,(e=>({propKey:t,error:e}))))}))}if(w){let t=Ue(e,w);t&&s.push(t)}if(b&&"array"===h){let t=Le(e,b);t&&s.push(t)}if(v&&"array"===h){let t=Be(e,v);t&&s.push({needRemove:!0,error:t})}if(y&&"array"===h){let t=De(e,y);t&&s.push(...t)}if(m||0===m){let t=Me(e,m);t&&s.push(t)}if(g){let t=Fe(e,g);t&&s.push(t)}if(p||0===p){let t=Ce(e,p);t&&s.push(t)}if(f||0===f){let t=ke(e,f);t&&s.push(t)}if("string"===h){let t;if(t="date-time"===d?Pe(e):"uuid"===d?Re(e):Ne(e),!t)return s;s.push(t)}else if("boolean"===h){let t=Ie(e);if(!t)return s;s.push(t)}else if("number"===h){let t=Oe(e);if(!t)return s;s.push(t)}else if("integer"===h){let t=je(e);if(!t)return s;s.push(t)}else if("array"===h){if(!A&&!C)return s;e&&c()(e).call(e,((e,n)=>{const o=ze(e,t.get("items"),!1,r,a);s.push(...i()(o).call(o,(e=>({index:n,error:e}))))}))}else if("file"===h){let t=Te(e);if(!t)return s;s.push(t)}return s}const qe=function(e,t){let{isOAS3:n=!1,bypassRequiredCheck:r=!1}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=e.get("required"),{schema:a,parameterContentMediaType:i}=(0,ne.Z)(e,{isOAS3:n});return ze(t,a,o,r,i)},$e=(e,t,n)=>{if(e&&!e.xml&&(e.xml={}),e&&!e.xml.name){if(!e.$$ref&&(e.type||e.items||e.properties||e.additionalProperties))return'\n\x3c!-- XML example cannot be generated; root element name is undefined --\x3e';if(e.$$ref){let t=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=t[1]}}return(0,Q.memoizedCreateXMLExample)(e,t,n)},Ve=[{when:/json/,shouldStringifyTypes:["string"]}],He=["object"],We=(e,t,n,r)=>{const o=(0,Q.memoizedSampleFromSchema)(e,t,r),a=typeof o,i=g()(Ve).call(Ve,((e,t)=>t.when.test(n)?[...e,...t.shouldStringifyTypes]:e),He);return J()(i,(e=>e===a))?C()(o,null,2):o},Je=(e,t,n,r)=>{const o=We(e,t,n,r);let a;try{a=se.ZP.dump(se.ZP.load(o),{lineWidth:-1},{schema:se.A8}),"\n"===a[a.length-1]&&(a=x()(a).call(a,0,a.length-1))}catch(e){return console.error(e),"error: could not generate yaml example"}return a.replace(/\t/g," ")},Ke=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:void 0;return e&&ge(e.toJS)&&(e=e.toJS()),r&&ge(r.toJS)&&(r=r.toJS()),/xml/.test(t)?$e(e,n,r):/(yaml|yml)/.test(t)?Je(e,n,t,r):We(e,n,t,r)},Ge=()=>{let e={},t=X.Z.location.search;if(!t)return{};if(""!=t){let n=t.substr(1).split("&");for(let t in n)Object.prototype.hasOwnProperty.call(n,t)&&(t=n[t].split("="),e[decodeURIComponent(t[0])]=t[1]&&decodeURIComponent(t[1])||"")}return e},Ze=e=>{let t;return t=e instanceof le?e:le.from(e.toString(),"utf-8"),t.toString("base64")},Ye={operationsSorter:{alpha:(e,t)=>e.get("path").localeCompare(t.get("path")),method:(e,t)=>e.get("method").localeCompare(t.get("method"))},tagsSorter:{alpha:(e,t)=>e.localeCompare(t)}},Qe=e=>{let t=[];for(let n in e){let r=e[n];void 0!==r&&""!==r&&t.push([n,"=",encodeURIComponent(r).replace(/%20/g,"+")].join(""))}return t.join("&")},Xe=(e,t,n)=>!!H()(n,(n=>G()(e[n],t[n])));function et(e){return"string"!=typeof e||""===e?"":(0,L.N)(e)}function tt(e){return!(!e||O()(e).call(e,"localhost")>=0||O()(e).call(e,"127.0.0.1")>=0||"none"===e)}function nt(e){if(!D().OrderedMap.isOrderedMap(e))return null;if(!e.size)return null;const t=T()(e).call(e,((e,t)=>N()(t).call(t,"2")&&d()(e.get("content")||{}).length>0)),n=e.get("default")||D().OrderedMap(),r=(n.get("content")||D().OrderedMap()).keySeq().toJS().length?n:null;return t||r}const rt=e=>"string"==typeof e||e instanceof String?R()(e).call(e).replace(/\s/g,"%20"):"",ot=e=>te()(rt(e).replace(/%20/g,"_")),at=e=>f()(e).call(e,((e,t)=>/^x-/.test(t))),it=e=>f()(e).call(e,((e,t)=>/^pattern|maxLength|minLength|maximum|minimum/.test(t)));function st(e,t){var n;let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:()=>!0;if("object"!=typeof e||o()(e)||null===e||!t)return e;const a=v()({},e);return c()(n=d()(a)).call(n,(e=>{e===t&&r(a[e],e)?delete a[e]:a[e]=st(a[e],t,r)})),a}function lt(e){if("string"==typeof e)return e;if(e&&e.toJS&&(e=e.toJS()),"object"==typeof e&&null!==e)try{return C()(e,null,2)}catch(t){return String(e)}return null==e?"":e.toString()}function ut(e){return"number"==typeof e?e.toString():e}function ct(e){let{returnAll:t=!1,allowHashes:n=!0}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!D().Map.isMap(e))throw new Error("paramToIdentifier: received a non-Im.Map parameter as input");const r=e.get("name"),o=e.get("in");let a=[];return e&&e.hashCode&&o&&r&&n&&a.push(`${o}.${r}.hash-${e.hashCode()}`),o&&r&&a.push(`${o}.${r}`),a.push(r),t?a:a[0]||""}function pt(e,t){var n;const r=ct(e,{returnAll:!0});return f()(n=i()(r).call(r,(e=>t[e]))).call(n,(e=>void 0!==e))[0]}function ft(){return dt(oe()(32).toString("base64"))}function ht(e){return dt(ie()("sha256").update(e).digest("base64"))}function dt(e){return e.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}const mt=e=>!e||!(!ce(e)||!e.isEmpty())},2518:(e,t,n)=>{"use strict";function r(e){return function(e){try{return!!JSON.parse(e)}catch(e){return null}}(e)?"json":null}n.d(t,{O:()=>r})},27504:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});const r=function(){var e={location:{},history:{},open:()=>{},close:()=>{},File:function(){}};if("undefined"==typeof window)return e;try{e=window;for(var t of["File","Blob","FormData"])t in window&&(e[t]=window[t])}catch(e){console.error(e)}return e}()},19069:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});var r=n(14418),o=n.n(r),a=n(58118),i=n.n(a),s=n(43393),l=n.n(s);const u=l().Set.of("type","format","items","default","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","maxItems","minItems","uniqueItems","enum","multipleOf");function c(e){let{isOAS3:t}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!l().Map.isMap(e))return{schema:l().Map(),parameterContentMediaType:null};if(!t)return"body"===e.get("in")?{schema:e.get("schema",l().Map()),parameterContentMediaType:null}:{schema:o()(e).call(e,((e,t)=>i()(u).call(u,t))),parameterContentMediaType:null};if(e.get("content")){const t=e.get("content",l().Map({})).keySeq().first();return{schema:e.getIn(["content",t,"schema"],l().Map()),parameterContentMediaType:t}}return{schema:e.get("schema")?e.get("schema",l().Map()):l().Map(),parameterContentMediaType:null}}},60314:(e,t,n)=>{"use strict";n.d(t,{Z:()=>x});var r=n(58309),o=n.n(r),a=n(2250),i=n.n(a),s=n(25110),l=n.n(s),u=n(8712),c=n.n(u),p=n(51679),f=n.n(p),h=n(12373),d=n.n(h),m=n(18492),g=n.n(m),y=n(88306),v=n.n(y);const b=e=>t=>o()(e)&&o()(t)&&e.length===t.length&&i()(e).call(e,((e,n)=>e===t[n])),w=function(){for(var e=arguments.length,t=new Array(e),n=0;n1&&void 0!==arguments[1]?arguments[1]:w;const{Cache:n}=v();v().Cache=E;const r=v()(e,t);return v().Cache=n,r}},79742:(e,t)=>{"use strict";t.byteLength=function(e){var t=l(e),n=t[0],r=t[1];return 3*(n+r)/4-r},t.toByteArray=function(e){var t,n,a=l(e),i=a[0],s=a[1],u=new o(function(e,t,n){return 3*(t+n)/4-n}(0,i,s)),c=0,p=s>0?i-4:i;for(n=0;n>16&255,u[c++]=t>>8&255,u[c++]=255&t;2===s&&(t=r[e.charCodeAt(n)]<<2|r[e.charCodeAt(n+1)]>>4,u[c++]=255&t);1===s&&(t=r[e.charCodeAt(n)]<<10|r[e.charCodeAt(n+1)]<<4|r[e.charCodeAt(n+2)]>>2,u[c++]=t>>8&255,u[c++]=255&t);return u},t.fromByteArray=function(e){for(var t,r=e.length,o=r%3,a=[],i=16383,s=0,l=r-o;sl?l:s+i));1===o?(t=e[r-1],a.push(n[t>>2]+n[t<<4&63]+"==")):2===o&&(t=(e[r-2]<<8)+e[r-1],a.push(n[t>>10]+n[t>>4&63]+n[t<<2&63]+"="));return a.join("")};for(var n=[],r=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",i=0,s=a.length;i0)throw new Error("Invalid string. Length must be a multiple of 4");var n=e.indexOf("=");return-1===n&&(n=t),[n,n===t?0:4-n%4]}function u(e,t,r){for(var o,a,i=[],s=t;s>18&63]+n[a>>12&63]+n[a>>6&63]+n[63&a]);return i.join("")}r["-".charCodeAt(0)]=62,r["_".charCodeAt(0)]=63},48764:(e,t,n)=>{"use strict";const r=n(79742),o=n(80645),a="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;t.Buffer=l,t.SlowBuffer=function(e){+e!=e&&(e=0);return l.alloc(+e)},t.INSPECT_MAX_BYTES=50;const i=2147483647;function s(e){if(e>i)throw new RangeError('The value "'+e+'" is invalid for option "size"');const t=new Uint8Array(e);return Object.setPrototypeOf(t,l.prototype),t}function l(e,t,n){if("number"==typeof e){if("string"==typeof t)throw new TypeError('The "string" argument must be of type string. Received type number');return p(e)}return u(e,t,n)}function u(e,t,n){if("string"==typeof e)return function(e,t){"string"==typeof t&&""!==t||(t="utf8");if(!l.isEncoding(t))throw new TypeError("Unknown encoding: "+t);const n=0|m(e,t);let r=s(n);const o=r.write(e,t);o!==n&&(r=r.slice(0,o));return r}(e,t);if(ArrayBuffer.isView(e))return function(e){if(G(e,Uint8Array)){const t=new Uint8Array(e);return h(t.buffer,t.byteOffset,t.byteLength)}return f(e)}(e);if(null==e)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e);if(G(e,ArrayBuffer)||e&&G(e.buffer,ArrayBuffer))return h(e,t,n);if("undefined"!=typeof SharedArrayBuffer&&(G(e,SharedArrayBuffer)||e&&G(e.buffer,SharedArrayBuffer)))return h(e,t,n);if("number"==typeof e)throw new TypeError('The "value" argument must not be of type number. Received type number');const r=e.valueOf&&e.valueOf();if(null!=r&&r!==e)return l.from(r,t,n);const o=function(e){if(l.isBuffer(e)){const t=0|d(e.length),n=s(t);return 0===n.length||e.copy(n,0,0,t),n}if(void 0!==e.length)return"number"!=typeof e.length||Z(e.length)?s(0):f(e);if("Buffer"===e.type&&Array.isArray(e.data))return f(e.data)}(e);if(o)return o;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof e[Symbol.toPrimitive])return l.from(e[Symbol.toPrimitive]("string"),t,n);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e)}function c(e){if("number"!=typeof e)throw new TypeError('"size" argument must be of type number');if(e<0)throw new RangeError('The value "'+e+'" is invalid for option "size"')}function p(e){return c(e),s(e<0?0:0|d(e))}function f(e){const t=e.length<0?0:0|d(e.length),n=s(t);for(let r=0;r=i)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+i.toString(16)+" bytes");return 0|e}function m(e,t){if(l.isBuffer(e))return e.length;if(ArrayBuffer.isView(e)||G(e,ArrayBuffer))return e.byteLength;if("string"!=typeof e)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof e);const n=e.length,r=arguments.length>2&&!0===arguments[2];if(!r&&0===n)return 0;let o=!1;for(;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":return W(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return J(e).length;default:if(o)return r?-1:W(e).length;t=(""+t).toLowerCase(),o=!0}}function g(e,t,n){let r=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return T(this,t,n);case"utf8":case"utf-8":return C(this,t,n);case"ascii":return O(this,t,n);case"latin1":case"binary":return j(this,t,n);case"base64":return A(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return I(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function y(e,t,n){const r=e[t];e[t]=e[n],e[n]=r}function v(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),Z(n=+n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=l.from(t,r)),l.isBuffer(t))return 0===t.length?-1:b(e,t,n,r,o);if("number"==typeof t)return t&=255,"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):b(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function b(e,t,n,r,o){let a,i=1,s=e.length,l=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;i=2,s/=2,l/=2,n/=2}function u(e,t){return 1===i?e[t]:e.readUInt16BE(t*i)}if(o){let r=-1;for(a=n;as&&(n=s-l),a=n;a>=0;a--){let n=!0;for(let r=0;ro&&(r=o):r=o;const a=t.length;let i;for(r>a/2&&(r=a/2),i=0;i>8,o=n%256,a.push(o),a.push(r);return a}(t,e.length-n),e,n,r)}function A(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function C(e,t,n){n=Math.min(e.length,n);const r=[];let o=t;for(;o239?4:t>223?3:t>191?2:1;if(o+i<=n){let n,r,s,l;switch(i){case 1:t<128&&(a=t);break;case 2:n=e[o+1],128==(192&n)&&(l=(31&t)<<6|63&n,l>127&&(a=l));break;case 3:n=e[o+1],r=e[o+2],128==(192&n)&&128==(192&r)&&(l=(15&t)<<12|(63&n)<<6|63&r,l>2047&&(l<55296||l>57343)&&(a=l));break;case 4:n=e[o+1],r=e[o+2],s=e[o+3],128==(192&n)&&128==(192&r)&&128==(192&s)&&(l=(15&t)<<18|(63&n)<<12|(63&r)<<6|63&s,l>65535&&l<1114112&&(a=l))}}null===a?(a=65533,i=1):a>65535&&(a-=65536,r.push(a>>>10&1023|55296),a=56320|1023&a),r.push(a),o+=i}return function(e){const t=e.length;if(t<=k)return String.fromCharCode.apply(String,e);let n="",r=0;for(;rr.length?(l.isBuffer(t)||(t=l.from(t)),t.copy(r,o)):Uint8Array.prototype.set.call(r,t,o);else{if(!l.isBuffer(t))throw new TypeError('"list" argument must be an Array of Buffers');t.copy(r,o)}o+=t.length}return r},l.byteLength=m,l.prototype._isBuffer=!0,l.prototype.swap16=function(){const e=this.length;if(e%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let t=0;tn&&(e+=" ... "),""},a&&(l.prototype[a]=l.prototype.inspect),l.prototype.compare=function(e,t,n,r,o){if(G(e,Uint8Array)&&(e=l.from(e,e.offset,e.byteLength)),!l.isBuffer(e))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof e);if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(this===e)return 0;let a=(o>>>=0)-(r>>>=0),i=(n>>>=0)-(t>>>=0);const s=Math.min(a,i),u=this.slice(r,o),c=e.slice(t,n);for(let e=0;e>>=0,isFinite(n)?(n>>>=0,void 0===r&&(r="utf8")):(r=n,n=void 0)}const o=this.length-t;if((void 0===n||n>o)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");let a=!1;for(;;)switch(r){case"hex":return w(this,e,t,n);case"utf8":case"utf-8":return E(this,e,t,n);case"ascii":case"latin1":case"binary":return x(this,e,t,n);case"base64":return _(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,e,t,n);default:if(a)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),a=!0}},l.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const k=4096;function O(e,t,n){let r="";n=Math.min(e.length,n);for(let o=t;or)&&(n=r);let o="";for(let r=t;rn)throw new RangeError("Trying to access beyond buffer length")}function P(e,t,n,r,o,a){if(!l.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||te.length)throw new RangeError("Index out of range")}function R(e,t,n,r,o){q(t,r,o,e,n,7);let a=Number(t&BigInt(4294967295));e[n++]=a,a>>=8,e[n++]=a,a>>=8,e[n++]=a,a>>=8,e[n++]=a;let i=Number(t>>BigInt(32)&BigInt(4294967295));return e[n++]=i,i>>=8,e[n++]=i,i>>=8,e[n++]=i,i>>=8,e[n++]=i,n}function M(e,t,n,r,o){q(t,r,o,e,n,7);let a=Number(t&BigInt(4294967295));e[n+7]=a,a>>=8,e[n+6]=a,a>>=8,e[n+5]=a,a>>=8,e[n+4]=a;let i=Number(t>>BigInt(32)&BigInt(4294967295));return e[n+3]=i,i>>=8,e[n+2]=i,i>>=8,e[n+1]=i,i>>=8,e[n]=i,n+8}function D(e,t,n,r,o,a){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function L(e,t,n,r,a){return t=+t,n>>>=0,a||D(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function B(e,t,n,r,a){return t=+t,n>>>=0,a||D(e,0,n,8),o.write(e,t,n,r,52,8),n+8}l.prototype.slice=function(e,t){const n=this.length;(e=~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),(t=void 0===t?n:~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),t>>=0,t>>>=0,n||N(e,t,this.length);let r=this[e],o=1,a=0;for(;++a>>=0,t>>>=0,n||N(e,t,this.length);let r=this[e+--t],o=1;for(;t>0&&(o*=256);)r+=this[e+--t]*o;return r},l.prototype.readUint8=l.prototype.readUInt8=function(e,t){return e>>>=0,t||N(e,1,this.length),this[e]},l.prototype.readUint16LE=l.prototype.readUInt16LE=function(e,t){return e>>>=0,t||N(e,2,this.length),this[e]|this[e+1]<<8},l.prototype.readUint16BE=l.prototype.readUInt16BE=function(e,t){return e>>>=0,t||N(e,2,this.length),this[e]<<8|this[e+1]},l.prototype.readUint32LE=l.prototype.readUInt32LE=function(e,t){return e>>>=0,t||N(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},l.prototype.readUint32BE=l.prototype.readUInt32BE=function(e,t){return e>>>=0,t||N(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},l.prototype.readBigUInt64LE=Q((function(e){$(e>>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||V(e,this.length-8);const r=t+256*this[++e]+65536*this[++e]+this[++e]*2**24,o=this[++e]+256*this[++e]+65536*this[++e]+n*2**24;return BigInt(r)+(BigInt(o)<>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||V(e,this.length-8);const r=t*2**24+65536*this[++e]+256*this[++e]+this[++e],o=this[++e]*2**24+65536*this[++e]+256*this[++e]+n;return(BigInt(r)<>>=0,t>>>=0,n||N(e,t,this.length);let r=this[e],o=1,a=0;for(;++a=o&&(r-=Math.pow(2,8*t)),r},l.prototype.readIntBE=function(e,t,n){e>>>=0,t>>>=0,n||N(e,t,this.length);let r=t,o=1,a=this[e+--r];for(;r>0&&(o*=256);)a+=this[e+--r]*o;return o*=128,a>=o&&(a-=Math.pow(2,8*t)),a},l.prototype.readInt8=function(e,t){return e>>>=0,t||N(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},l.prototype.readInt16LE=function(e,t){e>>>=0,t||N(e,2,this.length);const n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt16BE=function(e,t){e>>>=0,t||N(e,2,this.length);const n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt32LE=function(e,t){return e>>>=0,t||N(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},l.prototype.readInt32BE=function(e,t){return e>>>=0,t||N(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},l.prototype.readBigInt64LE=Q((function(e){$(e>>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||V(e,this.length-8);const r=this[e+4]+256*this[e+5]+65536*this[e+6]+(n<<24);return(BigInt(r)<>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||V(e,this.length-8);const r=(t<<24)+65536*this[++e]+256*this[++e]+this[++e];return(BigInt(r)<>>=0,t||N(e,4,this.length),o.read(this,e,!0,23,4)},l.prototype.readFloatBE=function(e,t){return e>>>=0,t||N(e,4,this.length),o.read(this,e,!1,23,4)},l.prototype.readDoubleLE=function(e,t){return e>>>=0,t||N(e,8,this.length),o.read(this,e,!0,52,8)},l.prototype.readDoubleBE=function(e,t){return e>>>=0,t||N(e,8,this.length),o.read(this,e,!1,52,8)},l.prototype.writeUintLE=l.prototype.writeUIntLE=function(e,t,n,r){if(e=+e,t>>>=0,n>>>=0,!r){P(this,e,t,n,Math.pow(2,8*n)-1,0)}let o=1,a=0;for(this[t]=255&e;++a>>=0,n>>>=0,!r){P(this,e,t,n,Math.pow(2,8*n)-1,0)}let o=n-1,a=1;for(this[t+o]=255&e;--o>=0&&(a*=256);)this[t+o]=e/a&255;return t+n},l.prototype.writeUint8=l.prototype.writeUInt8=function(e,t,n){return e=+e,t>>>=0,n||P(this,e,t,1,255,0),this[t]=255&e,t+1},l.prototype.writeUint16LE=l.prototype.writeUInt16LE=function(e,t,n){return e=+e,t>>>=0,n||P(this,e,t,2,65535,0),this[t]=255&e,this[t+1]=e>>>8,t+2},l.prototype.writeUint16BE=l.prototype.writeUInt16BE=function(e,t,n){return e=+e,t>>>=0,n||P(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=255&e,t+2},l.prototype.writeUint32LE=l.prototype.writeUInt32LE=function(e,t,n){return e=+e,t>>>=0,n||P(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e,t+4},l.prototype.writeUint32BE=l.prototype.writeUInt32BE=function(e,t,n){return e=+e,t>>>=0,n||P(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},l.prototype.writeBigUInt64LE=Q((function(e,t=0){return R(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))})),l.prototype.writeBigUInt64BE=Q((function(e,t=0){return M(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))})),l.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t>>>=0,!r){const r=Math.pow(2,8*n-1);P(this,e,t,n,r-1,-r)}let o=0,a=1,i=0;for(this[t]=255&e;++o>0)-i&255;return t+n},l.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t>>>=0,!r){const r=Math.pow(2,8*n-1);P(this,e,t,n,r-1,-r)}let o=n-1,a=1,i=0;for(this[t+o]=255&e;--o>=0&&(a*=256);)e<0&&0===i&&0!==this[t+o+1]&&(i=1),this[t+o]=(e/a>>0)-i&255;return t+n},l.prototype.writeInt8=function(e,t,n){return e=+e,t>>>=0,n||P(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=255&e,t+1},l.prototype.writeInt16LE=function(e,t,n){return e=+e,t>>>=0,n||P(this,e,t,2,32767,-32768),this[t]=255&e,this[t+1]=e>>>8,t+2},l.prototype.writeInt16BE=function(e,t,n){return e=+e,t>>>=0,n||P(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=255&e,t+2},l.prototype.writeInt32LE=function(e,t,n){return e=+e,t>>>=0,n||P(this,e,t,4,2147483647,-2147483648),this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},l.prototype.writeInt32BE=function(e,t,n){return e=+e,t>>>=0,n||P(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},l.prototype.writeBigInt64LE=Q((function(e,t=0){return R(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),l.prototype.writeBigInt64BE=Q((function(e,t=0){return M(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),l.prototype.writeFloatLE=function(e,t,n){return L(this,e,t,!0,n)},l.prototype.writeFloatBE=function(e,t,n){return L(this,e,t,!1,n)},l.prototype.writeDoubleLE=function(e,t,n){return B(this,e,t,!0,n)},l.prototype.writeDoubleBE=function(e,t,n){return B(this,e,t,!1,n)},l.prototype.copy=function(e,t,n,r){if(!l.isBuffer(e))throw new TypeError("argument should be a Buffer");if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(o=t;o=r+4;n-=3)t=`_${e.slice(n-3,n)}${t}`;return`${e.slice(0,n)}${t}`}function q(e,t,n,r,o,a){if(e>n||e3?0===t||t===BigInt(0)?`>= 0${r} and < 2${r} ** ${8*(a+1)}${r}`:`>= -(2${r} ** ${8*(a+1)-1}${r}) and < 2 ** ${8*(a+1)-1}${r}`:`>= ${t}${r} and <= ${n}${r}`,new F.ERR_OUT_OF_RANGE("value",o,e)}!function(e,t,n){$(t,"offset"),void 0!==e[t]&&void 0!==e[t+n]||V(t,e.length-(n+1))}(r,o,a)}function $(e,t){if("number"!=typeof e)throw new F.ERR_INVALID_ARG_TYPE(t,"number",e)}function V(e,t,n){if(Math.floor(e)!==e)throw $(e,n),new F.ERR_OUT_OF_RANGE(n||"offset","an integer",e);if(t<0)throw new F.ERR_BUFFER_OUT_OF_BOUNDS;throw new F.ERR_OUT_OF_RANGE(n||"offset",`>= ${n?1:0} and <= ${t}`,e)}U("ERR_BUFFER_OUT_OF_BOUNDS",(function(e){return e?`${e} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"}),RangeError),U("ERR_INVALID_ARG_TYPE",(function(e,t){return`The "${e}" argument must be of type number. Received type ${typeof t}`}),TypeError),U("ERR_OUT_OF_RANGE",(function(e,t,n){let r=`The value of "${e}" is out of range.`,o=n;return Number.isInteger(n)&&Math.abs(n)>2**32?o=z(String(n)):"bigint"==typeof n&&(o=String(n),(n>BigInt(2)**BigInt(32)||n<-(BigInt(2)**BigInt(32)))&&(o=z(o)),o+="n"),r+=` It must be ${t}. Received ${o}`,r}),RangeError);const H=/[^+/0-9A-Za-z-_]/g;function W(e,t){let n;t=t||1/0;const r=e.length;let o=null;const a=[];for(let i=0;i55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&a.push(239,191,189);continue}if(i+1===r){(t-=3)>-1&&a.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&a.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&a.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;a.push(n)}else if(n<2048){if((t-=2)<0)break;a.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;a.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;a.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return a}function J(e){return r.toByteArray(function(e){if((e=(e=e.split("=")[0]).trim().replace(H,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function K(e,t,n,r){let o;for(o=0;o=t.length||o>=e.length);++o)t[o+n]=e[o];return o}function G(e,t){return e instanceof t||null!=e&&null!=e.constructor&&null!=e.constructor.name&&e.constructor.name===t.name}function Z(e){return e!=e}const Y=function(){const e="0123456789abcdef",t=new Array(256);for(let n=0;n<16;++n){const r=16*n;for(let o=0;o<16;++o)t[r+o]=e[n]+e[o]}return t}();function Q(e){return"undefined"==typeof BigInt?X:e}function X(){throw new Error("BigInt not supported")}},21924:(e,t,n)=>{"use strict";var r=n(40210),o=n(55559),a=o(r("String.prototype.indexOf"));e.exports=function(e,t){var n=r(e,!!t);return"function"==typeof n&&a(e,".prototype.")>-1?o(n):n}},55559:(e,t,n)=>{"use strict";var r=n(58612),o=n(40210),a=o("%Function.prototype.apply%"),i=o("%Function.prototype.call%"),s=o("%Reflect.apply%",!0)||r.call(i,a),l=o("%Object.getOwnPropertyDescriptor%",!0),u=o("%Object.defineProperty%",!0),c=o("%Math.max%");if(u)try{u({},"a",{value:1})}catch(e){u=null}e.exports=function(e){var t=s(r,i,arguments);l&&u&&(l(t,"length").configurable&&u(t,"length",{value:1+c(0,e.length-(arguments.length-1))}));return t};var p=function(){return s(r,a,arguments)};u?u(e.exports,"apply",{value:p}):e.exports.apply=p},94184:(e,t)=>{var n;!function(){"use strict";var r={}.hasOwnProperty;function o(){for(var e=[],t=0;t{"use strict";t.parse=function(e,t){if("string"!=typeof e)throw new TypeError("argument str must be a string");var n={},r=(t||{}).decode||o,a=0;for(;a{"use strict";var r=n(11742),o={"text/plain":"Text","text/html":"Url",default:"Text"},a="Copy to clipboard: #{key}, Enter";e.exports=function(e,t){var n,i,s,l,u,c,p=!1;t||(t={}),n=t.debug||!1;try{if(s=r(),l=document.createRange(),u=document.getSelection(),(c=document.createElement("span")).textContent=e,c.ariaHidden="true",c.style.all="unset",c.style.position="fixed",c.style.top=0,c.style.clip="rect(0, 0, 0, 0)",c.style.whiteSpace="pre",c.style.webkitUserSelect="text",c.style.MozUserSelect="text",c.style.msUserSelect="text",c.style.userSelect="text",c.addEventListener("copy",(function(r){if(r.stopPropagation(),t.format)if(r.preventDefault(),void 0===r.clipboardData){n&&console.warn("unable to use e.clipboardData"),n&&console.warn("trying IE specific stuff"),window.clipboardData.clearData();var a=o[t.format]||o.default;window.clipboardData.setData(a,e)}else r.clipboardData.clearData(),r.clipboardData.setData(t.format,e);t.onCopy&&(r.preventDefault(),t.onCopy(r.clipboardData))})),document.body.appendChild(c),l.selectNodeContents(c),u.addRange(l),!document.execCommand("copy"))throw new Error("copy command was unsuccessful");p=!0}catch(r){n&&console.error("unable to copy using execCommand: ",r),n&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(t.format||"text",e),t.onCopy&&t.onCopy(window.clipboardData),p=!0}catch(r){n&&console.error("unable to copy using clipboardData: ",r),n&&console.error("falling back to prompt"),i=function(e){var t=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C";return e.replace(/#{\s*key\s*}/g,t)}("message"in t?t.message:a),window.prompt(i,e)}}finally{u&&("function"==typeof u.removeRange?u.removeRange(l):u.removeAllRanges()),c&&document.body.removeChild(c),s()}return p}},90093:(e,t,n)=>{var r=n(28196);e.exports=r},3688:(e,t,n)=>{var r=n(11955);e.exports=r},83838:(e,t,n)=>{var r=n(46279);e.exports=r},15684:(e,t,n)=>{var r=n(19373);e.exports=r},65362:(e,t,n)=>{var r=n(63383);e.exports=r},91254:(e,t,n)=>{var r=n(57396);e.exports=r},43536:(e,t,n)=>{var r=n(41910);e.exports=r},37331:(e,t,n)=>{var r=n(79427);e.exports=r},68522:(e,t,n)=>{var r=n(62857);e.exports=r},73151:(e,t,n)=>{var r=n(9534);e.exports=r},45012:(e,t,n)=>{var r=n(23059);e.exports=r},80281:(e,t,n)=>{var r=n(92547);n(43975),e.exports=r},40031:(e,t,n)=>{var r=n(46509);e.exports=r},17487:(e,t,n)=>{var r=n(35774);e.exports=r},54493:(e,t,n)=>{n(77971),n(53242);var r=n(54058);e.exports=r.Array.from},24034:(e,t,n)=>{n(92737);var r=n(54058);e.exports=r.Array.isArray},15367:(e,t,n)=>{n(85906);var r=n(35703);e.exports=r("Array").concat},12710:(e,t,n)=>{n(66274),n(55967);var r=n(35703);e.exports=r("Array").entries},51459:(e,t,n)=>{n(48851);var r=n(35703);e.exports=r("Array").every},6172:(e,t,n)=>{n(80290);var r=n(35703);e.exports=r("Array").fill},62383:(e,t,n)=>{n(21501);var r=n(35703);e.exports=r("Array").filter},60009:(e,t,n)=>{n(44929);var r=n(35703);e.exports=r("Array").findIndex},17671:(e,t,n)=>{n(80833);var r=n(35703);e.exports=r("Array").find},99324:(e,t,n)=>{n(2437);var r=n(35703);e.exports=r("Array").forEach},80991:(e,t,n)=>{n(97690);var r=n(35703);e.exports=r("Array").includes},8700:(e,t,n)=>{n(99076);var r=n(35703);e.exports=r("Array").indexOf},95909:(e,t,n)=>{n(66274),n(55967);var r=n(35703);e.exports=r("Array").keys},6442:(e,t,n)=>{n(75915);var r=n(35703);e.exports=r("Array").lastIndexOf},23866:(e,t,n)=>{n(68787);var r=n(35703);e.exports=r("Array").map},52999:(e,t,n)=>{n(81876);var r=n(35703);e.exports=r("Array").reduce},24900:(e,t,n)=>{n(60186);var r=n(35703);e.exports=r("Array").slice},3824:(e,t,n)=>{n(36026);var r=n(35703);e.exports=r("Array").some},2948:(e,t,n)=>{n(4115);var r=n(35703);e.exports=r("Array").sort},78209:(e,t,n)=>{n(98611);var r=n(35703);e.exports=r("Array").splice},14423:(e,t,n)=>{n(66274),n(55967);var r=n(35703);e.exports=r("Array").values},81103:(e,t,n)=>{n(95160);var r=n(54058);e.exports=r.Date.now},27700:(e,t,n)=>{n(73381);var r=n(35703);e.exports=r("Function").bind},16246:(e,t,n)=>{var r=n(7046),o=n(27700),a=Function.prototype;e.exports=function(e){var t=e.bind;return e===a||r(a,e)&&t===a.bind?o:t}},56043:(e,t,n)=>{var r=n(7046),o=n(15367),a=Array.prototype;e.exports=function(e){var t=e.concat;return e===a||r(a,e)&&t===a.concat?o:t}},13160:(e,t,n)=>{var r=n(7046),o=n(51459),a=Array.prototype;e.exports=function(e){var t=e.every;return e===a||r(a,e)&&t===a.every?o:t}},80446:(e,t,n)=>{var r=n(7046),o=n(6172),a=Array.prototype;e.exports=function(e){var t=e.fill;return e===a||r(a,e)&&t===a.fill?o:t}},2480:(e,t,n)=>{var r=n(7046),o=n(62383),a=Array.prototype;e.exports=function(e){var t=e.filter;return e===a||r(a,e)&&t===a.filter?o:t}},7147:(e,t,n)=>{var r=n(7046),o=n(60009),a=Array.prototype;e.exports=function(e){var t=e.findIndex;return e===a||r(a,e)&&t===a.findIndex?o:t}},32236:(e,t,n)=>{var r=n(7046),o=n(17671),a=Array.prototype;e.exports=function(e){var t=e.find;return e===a||r(a,e)&&t===a.find?o:t}},58557:(e,t,n)=>{var r=n(7046),o=n(80991),a=n(21631),i=Array.prototype,s=String.prototype;e.exports=function(e){var t=e.includes;return e===i||r(i,e)&&t===i.includes?o:"string"==typeof e||e===s||r(s,e)&&t===s.includes?a:t}},34570:(e,t,n)=>{var r=n(7046),o=n(8700),a=Array.prototype;e.exports=function(e){var t=e.indexOf;return e===a||r(a,e)&&t===a.indexOf?o:t}},57564:(e,t,n)=>{var r=n(7046),o=n(6442),a=Array.prototype;e.exports=function(e){var t=e.lastIndexOf;return e===a||r(a,e)&&t===a.lastIndexOf?o:t}},88287:(e,t,n)=>{var r=n(7046),o=n(23866),a=Array.prototype;e.exports=function(e){var t=e.map;return e===a||r(a,e)&&t===a.map?o:t}},68025:(e,t,n)=>{var r=n(7046),o=n(52999),a=Array.prototype;e.exports=function(e){var t=e.reduce;return e===a||r(a,e)&&t===a.reduce?o:t}},59257:(e,t,n)=>{var r=n(7046),o=n(80454),a=String.prototype;e.exports=function(e){var t=e.repeat;return"string"==typeof e||e===a||r(a,e)&&t===a.repeat?o:t}},69601:(e,t,n)=>{var r=n(7046),o=n(24900),a=Array.prototype;e.exports=function(e){var t=e.slice;return e===a||r(a,e)&&t===a.slice?o:t}},28299:(e,t,n)=>{var r=n(7046),o=n(3824),a=Array.prototype;e.exports=function(e){var t=e.some;return e===a||r(a,e)&&t===a.some?o:t}},69355:(e,t,n)=>{var r=n(7046),o=n(2948),a=Array.prototype;e.exports=function(e){var t=e.sort;return e===a||r(a,e)&&t===a.sort?o:t}},18339:(e,t,n)=>{var r=n(7046),o=n(78209),a=Array.prototype;e.exports=function(e){var t=e.splice;return e===a||r(a,e)&&t===a.splice?o:t}},71611:(e,t,n)=>{var r=n(7046),o=n(3269),a=String.prototype;e.exports=function(e){var t=e.startsWith;return"string"==typeof e||e===a||r(a,e)&&t===a.startsWith?o:t}},62774:(e,t,n)=>{var r=n(7046),o=n(13348),a=String.prototype;e.exports=function(e){var t=e.trim;return"string"==typeof e||e===a||r(a,e)&&t===a.trim?o:t}},84426:(e,t,n)=>{n(32619);var r=n(54058),o=n(79730);r.JSON||(r.JSON={stringify:JSON.stringify}),e.exports=function(e,t,n){return o(r.JSON.stringify,null,arguments)}},91018:(e,t,n)=>{n(66274),n(37501),n(55967),n(77971);var r=n(54058);e.exports=r.Map},45999:(e,t,n)=>{n(49221);var r=n(54058);e.exports=r.Object.assign},7702:(e,t,n)=>{n(74979);var r=n(54058).Object,o=e.exports=function(e,t){return r.defineProperties(e,t)};r.defineProperties.sham&&(o.sham=!0)},48171:(e,t,n)=>{n(86450);var r=n(54058).Object,o=e.exports=function(e,t,n){return r.defineProperty(e,t,n)};r.defineProperty.sham&&(o.sham=!0)},286:(e,t,n)=>{n(46924);var r=n(54058).Object,o=e.exports=function(e,t){return r.getOwnPropertyDescriptor(e,t)};r.getOwnPropertyDescriptor.sham&&(o.sham=!0)},92766:(e,t,n)=>{n(88482);var r=n(54058);e.exports=r.Object.getOwnPropertyDescriptors},30498:(e,t,n)=>{n(35824);var r=n(54058);e.exports=r.Object.getOwnPropertySymbols},48494:(e,t,n)=>{n(21724);var r=n(54058);e.exports=r.Object.keys},98430:(e,t,n)=>{n(26614);var r=n(54058);e.exports=r.Object.values},52956:(e,t,n)=>{n(47627),n(66274),n(55967),n(98881),n(4560),n(91302),n(44349),n(77971);var r=n(54058);e.exports=r.Promise},21631:(e,t,n)=>{n(11035);var r=n(35703);e.exports=r("String").includes},80454:(e,t,n)=>{n(60986);var r=n(35703);e.exports=r("String").repeat},3269:(e,t,n)=>{n(94761);var r=n(35703);e.exports=r("String").startsWith},13348:(e,t,n)=>{n(57398);var r=n(35703);e.exports=r("String").trim},57473:(e,t,n)=>{n(85906),n(55967),n(35824),n(8555),n(52615),n(21732),n(35903),n(1825),n(28394),n(45915),n(61766),n(62737),n(89911),n(74315),n(63131),n(64714),n(70659),n(69120),n(79413),n(1502);var r=n(54058);e.exports=r.Symbol},24227:(e,t,n)=>{n(66274),n(55967),n(77971),n(1825);var r=n(11477);e.exports=r.f("iterator")},62978:(e,t,n)=>{n(18084),n(63131);var r=n(11477);e.exports=r.f("toPrimitive")},14122:(e,t,n)=>{e.exports=n(89097)},44442:(e,t,n)=>{e.exports=n(51675)},57152:(e,t,n)=>{e.exports=n(82507)},69447:(e,t,n)=>{e.exports=n(628)},60269:(e,t,n)=>{e.exports=n(76936)},70573:(e,t,n)=>{e.exports=n(18180)},73685:(e,t,n)=>{e.exports=n(80621)},27533:(e,t,n)=>{e.exports=n(22948)},39057:(e,t,n)=>{e.exports=n(82108)},84710:(e,t,n)=>{e.exports=n(14058)},93799:(e,t,n)=>{e.exports=n(92093)},86600:(e,t,n)=>{e.exports=n(52201)},9759:(e,t,n)=>{e.exports=n(27398)},71384:(e,t,n)=>{e.exports=n(26189)},89097:(e,t,n)=>{var r=n(90093);e.exports=r},51675:(e,t,n)=>{var r=n(3688);e.exports=r},82507:(e,t,n)=>{var r=n(83838);e.exports=r},628:(e,t,n)=>{var r=n(15684);e.exports=r},76936:(e,t,n)=>{var r=n(65362);e.exports=r},18180:(e,t,n)=>{var r=n(91254);e.exports=r},80621:(e,t,n)=>{var r=n(43536);e.exports=r},22948:(e,t,n)=>{var r=n(37331);e.exports=r},82108:(e,t,n)=>{var r=n(68522);e.exports=r},14058:(e,t,n)=>{var r=n(73151);e.exports=r},92093:(e,t,n)=>{var r=n(45012);e.exports=r},52201:(e,t,n)=>{var r=n(80281);n(28783),n(22731),n(85605),n(65799),n(31943),n(46774),n(45414),n(80620),n(36172),e.exports=r},27398:(e,t,n)=>{var r=n(40031);e.exports=r},26189:(e,t,n)=>{var r=n(17487);e.exports=r},24883:(e,t,n)=>{var r=n(57475),o=n(69826),a=TypeError;e.exports=function(e){if(r(e))return e;throw a(o(e)+" is not a function")}},174:(e,t,n)=>{var r=n(24284),o=n(69826),a=TypeError;e.exports=function(e){if(r(e))return e;throw a(o(e)+" is not a constructor")}},11851:(e,t,n)=>{var r=n(57475),o=String,a=TypeError;e.exports=function(e){if("object"==typeof e||r(e))return e;throw a("Can't set "+o(e)+" as a prototype")}},18479:e=>{e.exports=function(){}},5743:(e,t,n)=>{var r=n(7046),o=TypeError;e.exports=function(e,t){if(r(t,e))return e;throw o("Incorrect invocation")}},96059:(e,t,n)=>{var r=n(10941),o=String,a=TypeError;e.exports=function(e){if(r(e))return e;throw a(o(e)+" is not an object")}},97135:(e,t,n)=>{var r=n(95981);e.exports=r((function(){if("function"==typeof ArrayBuffer){var e=new ArrayBuffer(8);Object.isExtensible(e)&&Object.defineProperty(e,"a",{value:8})}}))},91860:(e,t,n)=>{"use strict";var r=n(89678),o=n(59413),a=n(10623);e.exports=function(e){for(var t=r(this),n=a(t),i=arguments.length,s=o(i>1?arguments[1]:void 0,n),l=i>2?arguments[2]:void 0,u=void 0===l?n:o(l,n);u>s;)t[s++]=e;return t}},56837:(e,t,n)=>{"use strict";var r=n(3610).forEach,o=n(34194)("forEach");e.exports=o?[].forEach:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0)}},11354:(e,t,n)=>{"use strict";var r=n(86843),o=n(78834),a=n(89678),i=n(75196),s=n(6782),l=n(24284),u=n(10623),c=n(55449),p=n(53476),f=n(22902),h=Array;e.exports=function(e){var t=a(e),n=l(this),d=arguments.length,m=d>1?arguments[1]:void 0,g=void 0!==m;g&&(m=r(m,d>2?arguments[2]:void 0));var y,v,b,w,E,x,_=f(t),S=0;if(!_||this===h&&s(_))for(y=u(t),v=n?new this(y):h(y);y>S;S++)x=g?m(t[S],S):t[S],c(v,S,x);else for(E=(w=p(t,_)).next,v=n?new this:[];!(b=o(E,w)).done;S++)x=g?i(w,m,[b.value,S],!0):b.value,c(v,S,x);return v.length=S,v}},31692:(e,t,n)=>{var r=n(74529),o=n(59413),a=n(10623),i=function(e){return function(t,n,i){var s,l=r(t),u=a(l),c=o(i,u);if(e&&n!=n){for(;u>c;)if((s=l[c++])!=s)return!0}else for(;u>c;c++)if((e||c in l)&&l[c]===n)return e||c||0;return!e&&-1}};e.exports={includes:i(!0),indexOf:i(!1)}},3610:(e,t,n)=>{var r=n(86843),o=n(95329),a=n(37026),i=n(89678),s=n(10623),l=n(64692),u=o([].push),c=function(e){var t=1==e,n=2==e,o=3==e,c=4==e,p=6==e,f=7==e,h=5==e||p;return function(d,m,g,y){for(var v,b,w=i(d),E=a(w),x=r(m,g),_=s(E),S=0,A=y||l,C=t?A(d,_):n||f?A(d,0):void 0;_>S;S++)if((h||S in E)&&(b=x(v=E[S],S,w),e))if(t)C[S]=b;else if(b)switch(e){case 3:return!0;case 5:return v;case 6:return S;case 2:u(C,v)}else switch(e){case 4:return!1;case 7:u(C,v)}return p?-1:o||c?c:C}};e.exports={forEach:c(0),map:c(1),filter:c(2),some:c(3),every:c(4),find:c(5),findIndex:c(6),filterReject:c(7)}},67145:(e,t,n)=>{"use strict";var r=n(79730),o=n(74529),a=n(62435),i=n(10623),s=n(34194),l=Math.min,u=[].lastIndexOf,c=!!u&&1/[1].lastIndexOf(1,-0)<0,p=s("lastIndexOf"),f=c||!p;e.exports=f?function(e){if(c)return r(u,this,arguments)||0;var t=o(this),n=i(t),s=n-1;for(arguments.length>1&&(s=l(s,a(arguments[1]))),s<0&&(s=n+s);s>=0;s--)if(s in t&&t[s]===e)return s||0;return-1}:u},50568:(e,t,n)=>{var r=n(95981),o=n(99813),a=n(53385),i=o("species");e.exports=function(e){return a>=51||!r((function(){var t=[];return(t.constructor={})[i]=function(){return{foo:1}},1!==t[e](Boolean).foo}))}},34194:(e,t,n)=>{"use strict";var r=n(95981);e.exports=function(e,t){var n=[][e];return!!n&&r((function(){n.call(null,t||function(){return 1},1)}))}},46499:(e,t,n)=>{var r=n(24883),o=n(89678),a=n(37026),i=n(10623),s=TypeError,l=function(e){return function(t,n,l,u){r(n);var c=o(t),p=a(c),f=i(c),h=e?f-1:0,d=e?-1:1;if(l<2)for(;;){if(h in p){u=p[h],h+=d;break}if(h+=d,e?h<0:f<=h)throw s("Reduce of empty array with no initial value")}for(;e?h>=0:f>h;h+=d)h in p&&(u=n(u,p[h],h,c));return u}};e.exports={left:l(!1),right:l(!0)}},89779:(e,t,n)=>{"use strict";var r=n(55746),o=n(1052),a=TypeError,i=Object.getOwnPropertyDescriptor,s=r&&!function(){if(void 0!==this)return!0;try{Object.defineProperty([],"length",{writable:!1}).length=1}catch(e){return e instanceof TypeError}}();e.exports=s?function(e,t){if(o(e)&&!i(e,"length").writable)throw a("Cannot set read only .length");return e.length=t}:function(e,t){return e.length=t}},15790:(e,t,n)=>{var r=n(59413),o=n(10623),a=n(55449),i=Array,s=Math.max;e.exports=function(e,t,n){for(var l=o(e),u=r(t,l),c=r(void 0===n?l:n,l),p=i(s(c-u,0)),f=0;u{var r=n(95329);e.exports=r([].slice)},61388:(e,t,n)=>{var r=n(15790),o=Math.floor,a=function(e,t){var n=e.length,l=o(n/2);return n<8?i(e,t):s(e,a(r(e,0,l),t),a(r(e,l),t),t)},i=function(e,t){for(var n,r,o=e.length,a=1;a0;)e[r]=e[--r];r!==a++&&(e[r]=n)}return e},s=function(e,t,n,r){for(var o=t.length,a=n.length,i=0,s=0;i{var r=n(1052),o=n(24284),a=n(10941),i=n(99813)("species"),s=Array;e.exports=function(e){var t;return r(e)&&(t=e.constructor,(o(t)&&(t===s||r(t.prototype))||a(t)&&null===(t=t[i]))&&(t=void 0)),void 0===t?s:t}},64692:(e,t,n)=>{var r=n(5693);e.exports=function(e,t){return new(r(e))(0===t?0:t)}},75196:(e,t,n)=>{var r=n(96059),o=n(7609);e.exports=function(e,t,n,a){try{return a?t(r(n)[0],n[1]):t(n)}catch(t){o(e,"throw",t)}}},21385:(e,t,n)=>{var r=n(99813)("iterator"),o=!1;try{var a=0,i={next:function(){return{done:!!a++}},return:function(){o=!0}};i[r]=function(){return this},Array.from(i,(function(){throw 2}))}catch(e){}e.exports=function(e,t){if(!t&&!o)return!1;var n=!1;try{var a={};a[r]=function(){return{next:function(){return{done:n=!0}}}},e(a)}catch(e){}return n}},82532:(e,t,n)=>{var r=n(95329),o=r({}.toString),a=r("".slice);e.exports=function(e){return a(o(e),8,-1)}},9697:(e,t,n)=>{var r=n(22885),o=n(57475),a=n(82532),i=n(99813)("toStringTag"),s=Object,l="Arguments"==a(function(){return arguments}());e.exports=r?a:function(e){var t,n,r;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=s(e),i))?n:l?a(t):"Object"==(r=a(t))&&o(t.callee)?"Arguments":r}},85616:(e,t,n)=>{"use strict";var r=n(29290),o=n(29202),a=n(94380),i=n(86843),s=n(5743),l=n(82119),u=n(93091),c=n(75105),p=n(23538),f=n(94431),h=n(55746),d=n(21647).fastKey,m=n(45402),g=m.set,y=m.getterFor;e.exports={getConstructor:function(e,t,n,c){var p=e((function(e,o){s(e,f),g(e,{type:t,index:r(null),first:void 0,last:void 0,size:0}),h||(e.size=0),l(o)||u(o,e[c],{that:e,AS_ENTRIES:n})})),f=p.prototype,m=y(t),v=function(e,t,n){var r,o,a=m(e),i=b(e,t);return i?i.value=n:(a.last=i={index:o=d(t,!0),key:t,value:n,previous:r=a.last,next:void 0,removed:!1},a.first||(a.first=i),r&&(r.next=i),h?a.size++:e.size++,"F"!==o&&(a.index[o]=i)),e},b=function(e,t){var n,r=m(e),o=d(t);if("F"!==o)return r.index[o];for(n=r.first;n;n=n.next)if(n.key==t)return n};return a(f,{clear:function(){for(var e=m(this),t=e.index,n=e.first;n;)n.removed=!0,n.previous&&(n.previous=n.previous.next=void 0),delete t[n.index],n=n.next;e.first=e.last=void 0,h?e.size=0:this.size=0},delete:function(e){var t=this,n=m(t),r=b(t,e);if(r){var o=r.next,a=r.previous;delete n.index[r.index],r.removed=!0,a&&(a.next=o),o&&(o.previous=a),n.first==r&&(n.first=o),n.last==r&&(n.last=a),h?n.size--:t.size--}return!!r},forEach:function(e){for(var t,n=m(this),r=i(e,arguments.length>1?arguments[1]:void 0);t=t?t.next:n.first;)for(r(t.value,t.key,this);t&&t.removed;)t=t.previous},has:function(e){return!!b(this,e)}}),a(f,n?{get:function(e){var t=b(this,e);return t&&t.value},set:function(e,t){return v(this,0===e?0:e,t)}}:{add:function(e){return v(this,e=0===e?0:e,e)}}),h&&o(f,"size",{configurable:!0,get:function(){return m(this).size}}),p},setStrong:function(e,t,n){var r=t+" Iterator",o=y(t),a=y(r);c(e,t,(function(e,t){g(this,{type:r,target:e,state:o(e),kind:t,last:void 0})}),(function(){for(var e=a(this),t=e.kind,n=e.last;n&&n.removed;)n=n.previous;return e.target&&(e.last=n=n?n.next:e.state.first)?p("keys"==t?n.key:"values"==t?n.value:[n.key,n.value],!1):(e.target=void 0,p(void 0,!0))}),n?"entries":"values",!n,!0),f(t)}}},24683:(e,t,n)=>{"use strict";var r=n(76887),o=n(21899),a=n(21647),i=n(95981),s=n(32029),l=n(93091),u=n(5743),c=n(57475),p=n(10941),f=n(90904),h=n(65988).f,d=n(3610).forEach,m=n(55746),g=n(45402),y=g.set,v=g.getterFor;e.exports=function(e,t,n){var g,b=-1!==e.indexOf("Map"),w=-1!==e.indexOf("Weak"),E=b?"set":"add",x=o[e],_=x&&x.prototype,S={};if(m&&c(x)&&(w||_.forEach&&!i((function(){(new x).entries().next()})))){var A=(g=t((function(t,n){y(u(t,A),{type:e,collection:new x}),null!=n&&l(n,t[E],{that:t,AS_ENTRIES:b})}))).prototype,C=v(e);d(["add","clear","delete","forEach","get","has","set","keys","values","entries"],(function(e){var t="add"==e||"set"==e;!(e in _)||w&&"clear"==e||s(A,e,(function(n,r){var o=C(this).collection;if(!t&&w&&!p(n))return"get"==e&&void 0;var a=o[e](0===n?0:n,r);return t?this:a}))})),w||h(A,"size",{configurable:!0,get:function(){return C(this).collection.size}})}else g=n.getConstructor(t,e,b,E),a.enable();return f(g,e,!1,!0),S[e]=g,r({global:!0,forced:!0},S),w||n.setStrong(g,e,b),g}},23489:(e,t,n)=>{var r=n(90953),o=n(31136),a=n(49677),i=n(65988);e.exports=function(e,t,n){for(var s=o(t),l=i.f,u=a.f,c=0;c{var r=n(99813)("match");e.exports=function(e){var t=/./;try{"/./"[e](t)}catch(n){try{return t[r]=!1,"/./"[e](t)}catch(e){}}return!1}},64160:(e,t,n)=>{var r=n(95981);e.exports=!r((function(){function e(){}return e.prototype.constructor=null,Object.getPrototypeOf(new e)!==e.prototype}))},23538:e=>{e.exports=function(e,t){return{value:e,done:t}}},32029:(e,t,n)=>{var r=n(55746),o=n(65988),a=n(31887);e.exports=r?function(e,t,n){return o.f(e,t,a(1,n))}:function(e,t,n){return e[t]=n,e}},31887:e=>{e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},55449:(e,t,n)=>{"use strict";var r=n(83894),o=n(65988),a=n(31887);e.exports=function(e,t,n){var i=r(t);i in e?o.f(e,i,a(0,n)):e[i]=n}},29202:(e,t,n)=>{var r=n(65988);e.exports=function(e,t,n){return r.f(e,t,n)}},95929:(e,t,n)=>{var r=n(32029);e.exports=function(e,t,n,o){return o&&o.enumerable?e[t]=n:r(e,t,n),e}},94380:(e,t,n)=>{var r=n(95929);e.exports=function(e,t,n){for(var o in t)n&&n.unsafe&&e[o]?e[o]=t[o]:r(e,o,t[o],n);return e}},75609:(e,t,n)=>{var r=n(21899),o=Object.defineProperty;e.exports=function(e,t){try{o(r,e,{value:t,configurable:!0,writable:!0})}catch(n){r[e]=t}return t}},15863:(e,t,n)=>{"use strict";var r=n(69826),o=TypeError;e.exports=function(e,t){if(!delete e[t])throw o("Cannot delete property "+r(t)+" of "+r(e))}},55746:(e,t,n)=>{var r=n(95981);e.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},76616:e=>{var t="object"==typeof document&&document.all,n=void 0===t&&void 0!==t;e.exports={all:t,IS_HTMLDDA:n}},61333:(e,t,n)=>{var r=n(21899),o=n(10941),a=r.document,i=o(a)&&o(a.createElement);e.exports=function(e){return i?a.createElement(e):{}}},66796:e=>{var t=TypeError;e.exports=function(e){if(e>9007199254740991)throw t("Maximum allowed index exceeded");return e}},63281:e=>{e.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},34342:(e,t,n)=>{var r=n(2861).match(/firefox\/(\d+)/i);e.exports=!!r&&+r[1]},23321:(e,t,n)=>{var r=n(48501),o=n(6049);e.exports=!r&&!o&&"object"==typeof window&&"object"==typeof document},56491:e=>{e.exports="function"==typeof Bun&&Bun&&"string"==typeof Bun.version},48501:e=>{e.exports="object"==typeof Deno&&Deno&&"object"==typeof Deno.version},81046:(e,t,n)=>{var r=n(2861);e.exports=/MSIE|Trident/.test(r)},4470:(e,t,n)=>{var r=n(2861);e.exports=/ipad|iphone|ipod/i.test(r)&&"undefined"!=typeof Pebble},22749:(e,t,n)=>{var r=n(2861);e.exports=/(?:ipad|iphone|ipod).*applewebkit/i.test(r)},6049:(e,t,n)=>{var r=n(34155),o=n(82532);e.exports=void 0!==r&&"process"==o(r)},58045:(e,t,n)=>{var r=n(2861);e.exports=/web0s(?!.*chrome)/i.test(r)},2861:e=>{e.exports="undefined"!=typeof navigator&&String(navigator.userAgent)||""},53385:(e,t,n)=>{var r,o,a=n(21899),i=n(2861),s=a.process,l=a.Deno,u=s&&s.versions||l&&l.version,c=u&&u.v8;c&&(o=(r=c.split("."))[0]>0&&r[0]<4?1:+(r[0]+r[1])),!o&&i&&(!(r=i.match(/Edge\/(\d+)/))||r[1]>=74)&&(r=i.match(/Chrome\/(\d+)/))&&(o=+r[1]),e.exports=o},18938:(e,t,n)=>{var r=n(2861).match(/AppleWebKit\/(\d+)\./);e.exports=!!r&&+r[1]},35703:(e,t,n)=>{var r=n(54058);e.exports=function(e){return r[e+"Prototype"]}},56759:e=>{e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},53995:(e,t,n)=>{var r=n(95329),o=Error,a=r("".replace),i=String(o("zxcasd").stack),s=/\n\s*at [^:]*:[^\n]*/,l=s.test(i);e.exports=function(e,t){if(l&&"string"==typeof e&&!o.prepareStackTrace)for(;t--;)e=a(e,s,"");return e}},79585:(e,t,n)=>{var r=n(32029),o=n(53995),a=n(18780),i=Error.captureStackTrace;e.exports=function(e,t,n,s){a&&(i?i(e,t):r(e,"stack",o(n,s)))}},18780:(e,t,n)=>{var r=n(95981),o=n(31887);e.exports=!r((function(){var e=Error("a");return!("stack"in e)||(Object.defineProperty(e,"stack",o(1,7)),7!==e.stack)}))},76887:(e,t,n)=>{"use strict";var r=n(21899),o=n(79730),a=n(97484),i=n(57475),s=n(49677).f,l=n(37252),u=n(54058),c=n(86843),p=n(32029),f=n(90953),h=function(e){var t=function(n,r,a){if(this instanceof t){switch(arguments.length){case 0:return new e;case 1:return new e(n);case 2:return new e(n,r)}return new e(n,r,a)}return o(e,this,arguments)};return t.prototype=e.prototype,t};e.exports=function(e,t){var n,o,d,m,g,y,v,b,w,E=e.target,x=e.global,_=e.stat,S=e.proto,A=x?r:_?r[E]:(r[E]||{}).prototype,C=x?u:u[E]||p(u,E,{})[E],k=C.prototype;for(m in t)o=!(n=l(x?m:E+(_?".":"#")+m,e.forced))&&A&&f(A,m),y=C[m],o&&(v=e.dontCallGetSet?(w=s(A,m))&&w.value:A[m]),g=o&&v?v:t[m],o&&typeof y==typeof g||(b=e.bind&&o?c(g,r):e.wrap&&o?h(g):S&&i(g)?a(g):g,(e.sham||g&&g.sham||y&&y.sham)&&p(b,"sham",!0),p(C,m,b),S&&(f(u,d=E+"Prototype")||p(u,d,{}),p(u[d],m,g),e.real&&k&&(n||!k[m])&&p(k,m,g)))}},95981:e=>{e.exports=function(e){try{return!!e()}catch(e){return!0}}},45602:(e,t,n)=>{var r=n(95981);e.exports=!r((function(){return Object.isExtensible(Object.preventExtensions({}))}))},79730:(e,t,n)=>{var r=n(18285),o=Function.prototype,a=o.apply,i=o.call;e.exports="object"==typeof Reflect&&Reflect.apply||(r?i.bind(a):function(){return i.apply(a,arguments)})},86843:(e,t,n)=>{var r=n(97484),o=n(24883),a=n(18285),i=r(r.bind);e.exports=function(e,t){return o(e),void 0===t?e:a?i(e,t):function(){return e.apply(t,arguments)}}},18285:(e,t,n)=>{var r=n(95981);e.exports=!r((function(){var e=function(){}.bind();return"function"!=typeof e||e.hasOwnProperty("prototype")}))},98308:(e,t,n)=>{"use strict";var r=n(95329),o=n(24883),a=n(10941),i=n(90953),s=n(93765),l=n(18285),u=Function,c=r([].concat),p=r([].join),f={},h=function(e,t,n){if(!i(f,t)){for(var r=[],o=0;o{var r=n(18285),o=Function.prototype.call;e.exports=r?o.bind(o):function(){return o.apply(o,arguments)}},79417:(e,t,n)=>{var r=n(55746),o=n(90953),a=Function.prototype,i=r&&Object.getOwnPropertyDescriptor,s=o(a,"name"),l=s&&"something"===function(){}.name,u=s&&(!r||r&&i(a,"name").configurable);e.exports={EXISTS:s,PROPER:l,CONFIGURABLE:u}},45526:(e,t,n)=>{var r=n(95329),o=n(24883);e.exports=function(e,t,n){try{return r(o(Object.getOwnPropertyDescriptor(e,t)[n]))}catch(e){}}},97484:(e,t,n)=>{var r=n(82532),o=n(95329);e.exports=function(e){if("Function"===r(e))return o(e)}},95329:(e,t,n)=>{var r=n(18285),o=Function.prototype,a=o.call,i=r&&o.bind.bind(a,a);e.exports=r?i:function(e){return function(){return a.apply(e,arguments)}}},626:(e,t,n)=>{var r=n(54058),o=n(21899),a=n(57475),i=function(e){return a(e)?e:void 0};e.exports=function(e,t){return arguments.length<2?i(r[e])||i(o[e]):r[e]&&r[e][t]||o[e]&&o[e][t]}},22902:(e,t,n)=>{var r=n(9697),o=n(14229),a=n(82119),i=n(12077),s=n(99813)("iterator");e.exports=function(e){if(!a(e))return o(e,s)||o(e,"@@iterator")||i[r(e)]}},53476:(e,t,n)=>{var r=n(78834),o=n(24883),a=n(96059),i=n(69826),s=n(22902),l=TypeError;e.exports=function(e,t){var n=arguments.length<2?s(e):t;if(o(n))return a(r(n,e));throw l(i(e)+" is not iterable")}},33323:(e,t,n)=>{var r=n(95329),o=n(1052),a=n(57475),i=n(82532),s=n(85803),l=r([].push);e.exports=function(e){if(a(e))return e;if(o(e)){for(var t=e.length,n=[],r=0;r{var r=n(24883),o=n(82119);e.exports=function(e,t){var n=e[t];return o(n)?void 0:r(n)}},21899:(e,t,n)=>{var r=function(e){return e&&e.Math==Math&&e};e.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof n.g&&n.g)||function(){return this}()||Function("return this")()},90953:(e,t,n)=>{var r=n(95329),o=n(89678),a=r({}.hasOwnProperty);e.exports=Object.hasOwn||function(e,t){return a(o(e),t)}},27748:e=>{e.exports={}},34845:e=>{e.exports=function(e,t){try{1==arguments.length?console.error(e):console.error(e,t)}catch(e){}}},15463:(e,t,n)=>{var r=n(626);e.exports=r("document","documentElement")},2840:(e,t,n)=>{var r=n(55746),o=n(95981),a=n(61333);e.exports=!r&&!o((function(){return 7!=Object.defineProperty(a("div"),"a",{get:function(){return 7}}).a}))},37026:(e,t,n)=>{var r=n(95329),o=n(95981),a=n(82532),i=Object,s=r("".split);e.exports=o((function(){return!i("z").propertyIsEnumerable(0)}))?function(e){return"String"==a(e)?s(e,""):i(e)}:i},81302:(e,t,n)=>{var r=n(95329),o=n(57475),a=n(63030),i=r(Function.toString);o(a.inspectSource)||(a.inspectSource=function(e){return i(e)}),e.exports=a.inspectSource},53794:(e,t,n)=>{var r=n(10941),o=n(32029);e.exports=function(e,t){r(t)&&"cause"in t&&o(e,"cause",t.cause)}},21647:(e,t,n)=>{var r=n(76887),o=n(95329),a=n(27748),i=n(10941),s=n(90953),l=n(65988).f,u=n(10946),c=n(684),p=n(91584),f=n(99418),h=n(45602),d=!1,m=f("meta"),g=0,y=function(e){l(e,m,{value:{objectID:"O"+g++,weakData:{}}})},v=e.exports={enable:function(){v.enable=function(){},d=!0;var e=u.f,t=o([].splice),n={};n[m]=1,e(n).length&&(u.f=function(n){for(var r=e(n),o=0,a=r.length;o{var r,o,a,i=n(47093),s=n(21899),l=n(10941),u=n(32029),c=n(90953),p=n(63030),f=n(44262),h=n(27748),d="Object already initialized",m=s.TypeError,g=s.WeakMap;if(i||p.state){var y=p.state||(p.state=new g);y.get=y.get,y.has=y.has,y.set=y.set,r=function(e,t){if(y.has(e))throw m(d);return t.facade=e,y.set(e,t),t},o=function(e){return y.get(e)||{}},a=function(e){return y.has(e)}}else{var v=f("state");h[v]=!0,r=function(e,t){if(c(e,v))throw m(d);return t.facade=e,u(e,v,t),t},o=function(e){return c(e,v)?e[v]:{}},a=function(e){return c(e,v)}}e.exports={set:r,get:o,has:a,enforce:function(e){return a(e)?o(e):r(e,{})},getterFor:function(e){return function(t){var n;if(!l(t)||(n=o(t)).type!==e)throw m("Incompatible receiver, "+e+" required");return n}}}},6782:(e,t,n)=>{var r=n(99813),o=n(12077),a=r("iterator"),i=Array.prototype;e.exports=function(e){return void 0!==e&&(o.Array===e||i[a]===e)}},1052:(e,t,n)=>{var r=n(82532);e.exports=Array.isArray||function(e){return"Array"==r(e)}},57475:(e,t,n)=>{var r=n(76616),o=r.all;e.exports=r.IS_HTMLDDA?function(e){return"function"==typeof e||e===o}:function(e){return"function"==typeof e}},24284:(e,t,n)=>{var r=n(95329),o=n(95981),a=n(57475),i=n(9697),s=n(626),l=n(81302),u=function(){},c=[],p=s("Reflect","construct"),f=/^\s*(?:class|function)\b/,h=r(f.exec),d=!f.exec(u),m=function(e){if(!a(e))return!1;try{return p(u,c,e),!0}catch(e){return!1}},g=function(e){if(!a(e))return!1;switch(i(e)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return d||!!h(f,l(e))}catch(e){return!0}};g.sham=!0,e.exports=!p||o((function(){var e;return m(m.call)||!m(Object)||!m((function(){e=!0}))||e}))?g:m},37252:(e,t,n)=>{var r=n(95981),o=n(57475),a=/#|\.prototype\./,i=function(e,t){var n=l[s(e)];return n==c||n!=u&&(o(t)?r(t):!!t)},s=i.normalize=function(e){return String(e).replace(a,".").toLowerCase()},l=i.data={},u=i.NATIVE="N",c=i.POLYFILL="P";e.exports=i},82119:e=>{e.exports=function(e){return null==e}},10941:(e,t,n)=>{var r=n(57475),o=n(76616),a=o.all;e.exports=o.IS_HTMLDDA?function(e){return"object"==typeof e?null!==e:r(e)||e===a}:function(e){return"object"==typeof e?null!==e:r(e)}},82529:e=>{e.exports=!0},60685:(e,t,n)=>{var r=n(10941),o=n(82532),a=n(99813)("match");e.exports=function(e){var t;return r(e)&&(void 0!==(t=e[a])?!!t:"RegExp"==o(e))}},56664:(e,t,n)=>{var r=n(626),o=n(57475),a=n(7046),i=n(32302),s=Object;e.exports=i?function(e){return"symbol"==typeof e}:function(e){var t=r("Symbol");return o(t)&&a(t.prototype,s(e))}},93091:(e,t,n)=>{var r=n(86843),o=n(78834),a=n(96059),i=n(69826),s=n(6782),l=n(10623),u=n(7046),c=n(53476),p=n(22902),f=n(7609),h=TypeError,d=function(e,t){this.stopped=e,this.result=t},m=d.prototype;e.exports=function(e,t,n){var g,y,v,b,w,E,x,_=n&&n.that,S=!(!n||!n.AS_ENTRIES),A=!(!n||!n.IS_RECORD),C=!(!n||!n.IS_ITERATOR),k=!(!n||!n.INTERRUPTED),O=r(t,_),j=function(e){return g&&f(g,"normal",e),new d(!0,e)},T=function(e){return S?(a(e),k?O(e[0],e[1],j):O(e[0],e[1])):k?O(e,j):O(e)};if(A)g=e.iterator;else if(C)g=e;else{if(!(y=p(e)))throw h(i(e)+" is not iterable");if(s(y)){for(v=0,b=l(e);b>v;v++)if((w=T(e[v]))&&u(m,w))return w;return new d(!1)}g=c(e,y)}for(E=A?e.next:g.next;!(x=o(E,g)).done;){try{w=T(x.value)}catch(e){f(g,"throw",e)}if("object"==typeof w&&w&&u(m,w))return w}return new d(!1)}},7609:(e,t,n)=>{var r=n(78834),o=n(96059),a=n(14229);e.exports=function(e,t,n){var i,s;o(e);try{if(!(i=a(e,"return"))){if("throw"===t)throw n;return n}i=r(i,e)}catch(e){s=!0,i=e}if("throw"===t)throw n;if(s)throw i;return o(i),n}},53847:(e,t,n)=>{"use strict";var r=n(35143).IteratorPrototype,o=n(29290),a=n(31887),i=n(90904),s=n(12077),l=function(){return this};e.exports=function(e,t,n,u){var c=t+" Iterator";return e.prototype=o(r,{next:a(+!u,n)}),i(e,c,!1,!0),s[c]=l,e}},75105:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),a=n(82529),i=n(79417),s=n(57475),l=n(53847),u=n(249),c=n(88929),p=n(90904),f=n(32029),h=n(95929),d=n(99813),m=n(12077),g=n(35143),y=i.PROPER,v=i.CONFIGURABLE,b=g.IteratorPrototype,w=g.BUGGY_SAFARI_ITERATORS,E=d("iterator"),x="keys",_="values",S="entries",A=function(){return this};e.exports=function(e,t,n,i,d,g,C){l(n,t,i);var k,O,j,T=function(e){if(e===d&&M)return M;if(!w&&e in P)return P[e];switch(e){case x:case _:case S:return function(){return new n(this,e)}}return function(){return new n(this)}},I=t+" Iterator",N=!1,P=e.prototype,R=P[E]||P["@@iterator"]||d&&P[d],M=!w&&R||T(d),D="Array"==t&&P.entries||R;if(D&&(k=u(D.call(new e)))!==Object.prototype&&k.next&&(a||u(k)===b||(c?c(k,b):s(k[E])||h(k,E,A)),p(k,I,!0,!0),a&&(m[I]=A)),y&&d==_&&R&&R.name!==_&&(!a&&v?f(P,"name",_):(N=!0,M=function(){return o(R,this)})),d)if(O={values:T(_),keys:g?M:T(x),entries:T(S)},C)for(j in O)(w||N||!(j in P))&&h(P,j,O[j]);else r({target:t,proto:!0,forced:w||N},O);return a&&!C||P[E]===M||h(P,E,M,{name:d}),m[t]=M,O}},35143:(e,t,n)=>{"use strict";var r,o,a,i=n(95981),s=n(57475),l=n(10941),u=n(29290),c=n(249),p=n(95929),f=n(99813),h=n(82529),d=f("iterator"),m=!1;[].keys&&("next"in(a=[].keys())?(o=c(c(a)))!==Object.prototype&&(r=o):m=!0),!l(r)||i((function(){var e={};return r[d].call(e)!==e}))?r={}:h&&(r=u(r)),s(r[d])||p(r,d,(function(){return this})),e.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:m}},12077:e=>{e.exports={}},10623:(e,t,n)=>{var r=n(43057);e.exports=function(e){return r(e.length)}},35331:e=>{var t=Math.ceil,n=Math.floor;e.exports=Math.trunc||function(e){var r=+e;return(r>0?n:t)(r)}},66132:(e,t,n)=>{var r,o,a,i,s,l=n(21899),u=n(86843),c=n(49677).f,p=n(42941).set,f=n(18397),h=n(22749),d=n(4470),m=n(58045),g=n(6049),y=l.MutationObserver||l.WebKitMutationObserver,v=l.document,b=l.process,w=l.Promise,E=c(l,"queueMicrotask"),x=E&&E.value;if(!x){var _=new f,S=function(){var e,t;for(g&&(e=b.domain)&&e.exit();t=_.get();)try{t()}catch(e){throw _.head&&r(),e}e&&e.enter()};h||g||m||!y||!v?!d&&w&&w.resolve?((i=w.resolve(void 0)).constructor=w,s=u(i.then,i),r=function(){s(S)}):g?r=function(){b.nextTick(S)}:(p=u(p,l),r=function(){p(S)}):(o=!0,a=v.createTextNode(""),new y(S).observe(a,{characterData:!0}),r=function(){a.data=o=!o}),x=function(e){_.head||r(),_.add(e)}}e.exports=x},69520:(e,t,n)=>{"use strict";var r=n(24883),o=TypeError,a=function(e){var t,n;this.promise=new e((function(e,r){if(void 0!==t||void 0!==n)throw o("Bad Promise constructor");t=e,n=r})),this.resolve=r(t),this.reject=r(n)};e.exports.f=function(e){return new a(e)}},14649:(e,t,n)=>{var r=n(85803);e.exports=function(e,t){return void 0===e?arguments.length<2?"":t:r(e)}},70344:(e,t,n)=>{var r=n(60685),o=TypeError;e.exports=function(e){if(r(e))throw o("The method doesn't accept regular expressions");return e}},24420:(e,t,n)=>{"use strict";var r=n(55746),o=n(95329),a=n(78834),i=n(95981),s=n(14771),l=n(87857),u=n(36760),c=n(89678),p=n(37026),f=Object.assign,h=Object.defineProperty,d=o([].concat);e.exports=!f||i((function(){if(r&&1!==f({b:1},f(h({},"a",{enumerable:!0,get:function(){h(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var e={},t={},n=Symbol(),o="abcdefghijklmnopqrst";return e[n]=7,o.split("").forEach((function(e){t[e]=e})),7!=f({},e)[n]||s(f({},t)).join("")!=o}))?function(e,t){for(var n=c(e),o=arguments.length,i=1,f=l.f,h=u.f;o>i;)for(var m,g=p(arguments[i++]),y=f?d(s(g),f(g)):s(g),v=y.length,b=0;v>b;)m=y[b++],r&&!a(h,g,m)||(n[m]=g[m]);return n}:f},29290:(e,t,n)=>{var r,o=n(96059),a=n(59938),i=n(56759),s=n(27748),l=n(15463),u=n(61333),c=n(44262),p="prototype",f="script",h=c("IE_PROTO"),d=function(){},m=function(e){return"<"+f+">"+e+""},g=function(e){e.write(m("")),e.close();var t=e.parentWindow.Object;return e=null,t},y=function(){try{r=new ActiveXObject("htmlfile")}catch(e){}var e,t,n;y="undefined"!=typeof document?document.domain&&r?g(r):(t=u("iframe"),n="java"+f+":",t.style.display="none",l.appendChild(t),t.src=String(n),(e=t.contentWindow.document).open(),e.write(m("document.F=Object")),e.close(),e.F):g(r);for(var o=i.length;o--;)delete y[p][i[o]];return y()};s[h]=!0,e.exports=Object.create||function(e,t){var n;return null!==e?(d[p]=o(e),n=new d,d[p]=null,n[h]=e):n=y(),void 0===t?n:a.f(n,t)}},59938:(e,t,n)=>{var r=n(55746),o=n(83937),a=n(65988),i=n(96059),s=n(74529),l=n(14771);t.f=r&&!o?Object.defineProperties:function(e,t){i(e);for(var n,r=s(t),o=l(t),u=o.length,c=0;u>c;)a.f(e,n=o[c++],r[n]);return e}},65988:(e,t,n)=>{var r=n(55746),o=n(2840),a=n(83937),i=n(96059),s=n(83894),l=TypeError,u=Object.defineProperty,c=Object.getOwnPropertyDescriptor,p="enumerable",f="configurable",h="writable";t.f=r?a?function(e,t,n){if(i(e),t=s(t),i(n),"function"==typeof e&&"prototype"===t&&"value"in n&&h in n&&!n[h]){var r=c(e,t);r&&r[h]&&(e[t]=n.value,n={configurable:f in n?n[f]:r[f],enumerable:p in n?n[p]:r[p],writable:!1})}return u(e,t,n)}:u:function(e,t,n){if(i(e),t=s(t),i(n),o)try{return u(e,t,n)}catch(e){}if("get"in n||"set"in n)throw l("Accessors not supported");return"value"in n&&(e[t]=n.value),e}},49677:(e,t,n)=>{var r=n(55746),o=n(78834),a=n(36760),i=n(31887),s=n(74529),l=n(83894),u=n(90953),c=n(2840),p=Object.getOwnPropertyDescriptor;t.f=r?p:function(e,t){if(e=s(e),t=l(t),c)try{return p(e,t)}catch(e){}if(u(e,t))return i(!o(a.f,e,t),e[t])}},684:(e,t,n)=>{var r=n(82532),o=n(74529),a=n(10946).f,i=n(15790),s="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];e.exports.f=function(e){return s&&"Window"==r(e)?function(e){try{return a(e)}catch(e){return i(s)}}(e):a(o(e))}},10946:(e,t,n)=>{var r=n(55629),o=n(56759).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return r(e,o)}},87857:(e,t)=>{t.f=Object.getOwnPropertySymbols},249:(e,t,n)=>{var r=n(90953),o=n(57475),a=n(89678),i=n(44262),s=n(64160),l=i("IE_PROTO"),u=Object,c=u.prototype;e.exports=s?u.getPrototypeOf:function(e){var t=a(e);if(r(t,l))return t[l];var n=t.constructor;return o(n)&&t instanceof n?n.prototype:t instanceof u?c:null}},91584:(e,t,n)=>{var r=n(95981),o=n(10941),a=n(82532),i=n(97135),s=Object.isExtensible,l=r((function(){s(1)}));e.exports=l||i?function(e){return!!o(e)&&((!i||"ArrayBuffer"!=a(e))&&(!s||s(e)))}:s},7046:(e,t,n)=>{var r=n(95329);e.exports=r({}.isPrototypeOf)},55629:(e,t,n)=>{var r=n(95329),o=n(90953),a=n(74529),i=n(31692).indexOf,s=n(27748),l=r([].push);e.exports=function(e,t){var n,r=a(e),u=0,c=[];for(n in r)!o(s,n)&&o(r,n)&&l(c,n);for(;t.length>u;)o(r,n=t[u++])&&(~i(c,n)||l(c,n));return c}},14771:(e,t,n)=>{var r=n(55629),o=n(56759);e.exports=Object.keys||function(e){return r(e,o)}},36760:(e,t)=>{"use strict";var n={}.propertyIsEnumerable,r=Object.getOwnPropertyDescriptor,o=r&&!n.call({1:2},1);t.f=o?function(e){var t=r(this,e);return!!t&&t.enumerable}:n},88929:(e,t,n)=>{var r=n(45526),o=n(96059),a=n(11851);e.exports=Object.setPrototypeOf||("__proto__"in{}?function(){var e,t=!1,n={};try{(e=r(Object.prototype,"__proto__","set"))(n,[]),t=n instanceof Array}catch(e){}return function(n,r){return o(n),a(r),t?e(n,r):n.__proto__=r,n}}():void 0)},88810:(e,t,n)=>{var r=n(55746),o=n(95329),a=n(14771),i=n(74529),s=o(n(36760).f),l=o([].push),u=function(e){return function(t){for(var n,o=i(t),u=a(o),c=u.length,p=0,f=[];c>p;)n=u[p++],r&&!s(o,n)||l(f,e?[n,o[n]]:o[n]);return f}};e.exports={entries:u(!0),values:u(!1)}},95623:(e,t,n)=>{"use strict";var r=n(22885),o=n(9697);e.exports=r?{}.toString:function(){return"[object "+o(this)+"]"}},39811:(e,t,n)=>{var r=n(78834),o=n(57475),a=n(10941),i=TypeError;e.exports=function(e,t){var n,s;if("string"===t&&o(n=e.toString)&&!a(s=r(n,e)))return s;if(o(n=e.valueOf)&&!a(s=r(n,e)))return s;if("string"!==t&&o(n=e.toString)&&!a(s=r(n,e)))return s;throw i("Can't convert object to primitive value")}},31136:(e,t,n)=>{var r=n(626),o=n(95329),a=n(10946),i=n(87857),s=n(96059),l=o([].concat);e.exports=r("Reflect","ownKeys")||function(e){var t=a.f(s(e)),n=i.f;return n?l(t,n(e)):t}},54058:e=>{e.exports={}},40002:e=>{e.exports=function(e){try{return{error:!1,value:e()}}catch(e){return{error:!0,value:e}}}},67742:(e,t,n)=>{var r=n(21899),o=n(6991),a=n(57475),i=n(37252),s=n(81302),l=n(99813),u=n(23321),c=n(48501),p=n(82529),f=n(53385),h=o&&o.prototype,d=l("species"),m=!1,g=a(r.PromiseRejectionEvent),y=i("Promise",(function(){var e=s(o),t=e!==String(o);if(!t&&66===f)return!0;if(p&&(!h.catch||!h.finally))return!0;if(!f||f<51||!/native code/.test(e)){var n=new o((function(e){e(1)})),r=function(e){e((function(){}),(function(){}))};if((n.constructor={})[d]=r,!(m=n.then((function(){}))instanceof r))return!0}return!t&&(u||c)&&!g}));e.exports={CONSTRUCTOR:y,REJECTION_EVENT:g,SUBCLASSING:m}},6991:(e,t,n)=>{var r=n(21899);e.exports=r.Promise},56584:(e,t,n)=>{var r=n(96059),o=n(10941),a=n(69520);e.exports=function(e,t){if(r(e),o(t)&&t.constructor===e)return t;var n=a.f(e);return(0,n.resolve)(t),n.promise}},31542:(e,t,n)=>{var r=n(6991),o=n(21385),a=n(67742).CONSTRUCTOR;e.exports=a||!o((function(e){r.all(e).then(void 0,(function(){}))}))},18397:e=>{var t=function(){this.head=null,this.tail=null};t.prototype={add:function(e){var t={item:e,next:null},n=this.tail;n?n.next=t:this.head=t,this.tail=t},get:function(){var e=this.head;if(e)return null===(this.head=e.next)&&(this.tail=null),e.item}},e.exports=t},48219:(e,t,n)=>{var r=n(82119),o=TypeError;e.exports=function(e){if(r(e))throw o("Can't call method on "+e);return e}},37620:(e,t,n)=>{"use strict";var r,o=n(21899),a=n(79730),i=n(57475),s=n(56491),l=n(2861),u=n(93765),c=n(18348),p=o.Function,f=/MSIE .\./.test(l)||s&&((r=o.Bun.version.split(".")).length<3||0==r[0]&&(r[1]<3||3==r[1]&&0==r[2]));e.exports=function(e,t){var n=t?2:1;return f?function(r,o){var s=c(arguments.length,1)>n,l=i(r)?r:p(r),f=s?u(arguments,n):[],h=s?function(){a(l,this,f)}:l;return t?e(h,o):e(h)}:e}},94431:(e,t,n)=>{"use strict";var r=n(626),o=n(29202),a=n(99813),i=n(55746),s=a("species");e.exports=function(e){var t=r(e);i&&t&&!t[s]&&o(t,s,{configurable:!0,get:function(){return this}})}},90904:(e,t,n)=>{var r=n(22885),o=n(65988).f,a=n(32029),i=n(90953),s=n(95623),l=n(99813)("toStringTag");e.exports=function(e,t,n,u){if(e){var c=n?e:e.prototype;i(c,l)||o(c,l,{configurable:!0,value:t}),u&&!r&&a(c,"toString",s)}}},44262:(e,t,n)=>{var r=n(68726),o=n(99418),a=r("keys");e.exports=function(e){return a[e]||(a[e]=o(e))}},63030:(e,t,n)=>{var r=n(21899),o=n(75609),a="__core-js_shared__",i=r[a]||o(a,{});e.exports=i},68726:(e,t,n)=>{var r=n(82529),o=n(63030);(e.exports=function(e,t){return o[e]||(o[e]=void 0!==t?t:{})})("versions",[]).push({version:"3.28.0",mode:r?"pure":"global",copyright:"© 2014-2023 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.28.0/LICENSE",source:"https://github.com/zloirock/core-js"})},70487:(e,t,n)=>{var r=n(96059),o=n(174),a=n(82119),i=n(99813)("species");e.exports=function(e,t){var n,s=r(e).constructor;return void 0===s||a(n=r(s)[i])?t:o(n)}},64620:(e,t,n)=>{var r=n(95329),o=n(62435),a=n(85803),i=n(48219),s=r("".charAt),l=r("".charCodeAt),u=r("".slice),c=function(e){return function(t,n){var r,c,p=a(i(t)),f=o(n),h=p.length;return f<0||f>=h?e?"":void 0:(r=l(p,f))<55296||r>56319||f+1===h||(c=l(p,f+1))<56320||c>57343?e?s(p,f):r:e?u(p,f,f+2):c-56320+(r-55296<<10)+65536}};e.exports={codeAt:c(!1),charAt:c(!0)}},73291:(e,t,n)=>{var r=n(95329),o=2147483647,a=/[^\0-\u007E]/,i=/[.\u3002\uFF0E\uFF61]/g,s="Overflow: input needs wider integers to process",l=RangeError,u=r(i.exec),c=Math.floor,p=String.fromCharCode,f=r("".charCodeAt),h=r([].join),d=r([].push),m=r("".replace),g=r("".split),y=r("".toLowerCase),v=function(e){return e+22+75*(e<26)},b=function(e,t,n){var r=0;for(e=n?c(e/700):e>>1,e+=c(e/t);e>455;)e=c(e/35),r+=36;return c(r+36*e/(e+38))},w=function(e){var t=[];e=function(e){for(var t=[],n=0,r=e.length;n=55296&&o<=56319&&n=i&&rc((o-u)/E))throw l(s);for(u+=(w-i)*E,i=w,n=0;no)throw l(s);if(r==i){for(var x=u,_=36;;){var S=_<=m?1:_>=m+26?26:_-m;if(x{"use strict";var r=n(62435),o=n(85803),a=n(48219),i=RangeError;e.exports=function(e){var t=o(a(this)),n="",s=r(e);if(s<0||s==1/0)throw i("Wrong number of repetitions");for(;s>0;(s>>>=1)&&(t+=t))1&s&&(n+=t);return n}},93093:(e,t,n)=>{var r=n(79417).PROPER,o=n(95981),a=n(73483);e.exports=function(e){return o((function(){return!!a[e]()||"​…᠎"!=="​…᠎"[e]()||r&&a[e].name!==e}))}},74853:(e,t,n)=>{var r=n(95329),o=n(48219),a=n(85803),i=n(73483),s=r("".replace),l=RegExp("^["+i+"]+"),u=RegExp("(^|[^"+i+"])["+i+"]+$"),c=function(e){return function(t){var n=a(o(t));return 1&e&&(n=s(n,l,"")),2&e&&(n=s(n,u,"$1")),n}};e.exports={start:c(1),end:c(2),trim:c(3)}},63405:(e,t,n)=>{var r=n(53385),o=n(95981);e.exports=!!Object.getOwnPropertySymbols&&!o((function(){var e=Symbol();return!String(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&r&&r<41}))},29630:(e,t,n)=>{var r=n(78834),o=n(626),a=n(99813),i=n(95929);e.exports=function(){var e=o("Symbol"),t=e&&e.prototype,n=t&&t.valueOf,s=a("toPrimitive");t&&!t[s]&&i(t,s,(function(e){return r(n,this)}),{arity:1})}},34680:(e,t,n)=>{var r=n(63405);e.exports=r&&!!Symbol.for&&!!Symbol.keyFor},42941:(e,t,n)=>{var r,o,a,i,s=n(21899),l=n(79730),u=n(86843),c=n(57475),p=n(90953),f=n(95981),h=n(15463),d=n(93765),m=n(61333),g=n(18348),y=n(22749),v=n(6049),b=s.setImmediate,w=s.clearImmediate,E=s.process,x=s.Dispatch,_=s.Function,S=s.MessageChannel,A=s.String,C=0,k={},O="onreadystatechange";f((function(){r=s.location}));var j=function(e){if(p(k,e)){var t=k[e];delete k[e],t()}},T=function(e){return function(){j(e)}},I=function(e){j(e.data)},N=function(e){s.postMessage(A(e),r.protocol+"//"+r.host)};b&&w||(b=function(e){g(arguments.length,1);var t=c(e)?e:_(e),n=d(arguments,1);return k[++C]=function(){l(t,void 0,n)},o(C),C},w=function(e){delete k[e]},v?o=function(e){E.nextTick(T(e))}:x&&x.now?o=function(e){x.now(T(e))}:S&&!y?(i=(a=new S).port2,a.port1.onmessage=I,o=u(i.postMessage,i)):s.addEventListener&&c(s.postMessage)&&!s.importScripts&&r&&"file:"!==r.protocol&&!f(N)?(o=N,s.addEventListener("message",I,!1)):o=O in m("script")?function(e){h.appendChild(m("script"))[O]=function(){h.removeChild(this),j(e)}}:function(e){setTimeout(T(e),0)}),e.exports={set:b,clear:w}},59413:(e,t,n)=>{var r=n(62435),o=Math.max,a=Math.min;e.exports=function(e,t){var n=r(e);return n<0?o(n+t,0):a(n,t)}},74529:(e,t,n)=>{var r=n(37026),o=n(48219);e.exports=function(e){return r(o(e))}},62435:(e,t,n)=>{var r=n(35331);e.exports=function(e){var t=+e;return t!=t||0===t?0:r(t)}},43057:(e,t,n)=>{var r=n(62435),o=Math.min;e.exports=function(e){return e>0?o(r(e),9007199254740991):0}},89678:(e,t,n)=>{var r=n(48219),o=Object;e.exports=function(e){return o(r(e))}},46935:(e,t,n)=>{var r=n(78834),o=n(10941),a=n(56664),i=n(14229),s=n(39811),l=n(99813),u=TypeError,c=l("toPrimitive");e.exports=function(e,t){if(!o(e)||a(e))return e;var n,l=i(e,c);if(l){if(void 0===t&&(t="default"),n=r(l,e,t),!o(n)||a(n))return n;throw u("Can't convert object to primitive value")}return void 0===t&&(t="number"),s(e,t)}},83894:(e,t,n)=>{var r=n(46935),o=n(56664);e.exports=function(e){var t=r(e,"string");return o(t)?t:t+""}},22885:(e,t,n)=>{var r={};r[n(99813)("toStringTag")]="z",e.exports="[object z]"===String(r)},85803:(e,t,n)=>{var r=n(9697),o=String;e.exports=function(e){if("Symbol"===r(e))throw TypeError("Cannot convert a Symbol value to a string");return o(e)}},69826:e=>{var t=String;e.exports=function(e){try{return t(e)}catch(e){return"Object"}}},99418:(e,t,n)=>{var r=n(95329),o=0,a=Math.random(),i=r(1..toString);e.exports=function(e){return"Symbol("+(void 0===e?"":e)+")_"+i(++o+a,36)}},14766:(e,t,n)=>{var r=n(95981),o=n(99813),a=n(82529),i=o("iterator");e.exports=!r((function(){var e=new URL("b?a=1&b=2&c=3","http://a"),t=e.searchParams,n="";return e.pathname="c%20d",t.forEach((function(e,r){t.delete("b"),n+=r+e})),a&&!e.toJSON||!t.sort||"http://a/c%20d?a=1&c=3"!==e.href||"3"!==t.get("c")||"a=1"!==String(new URLSearchParams("?a=1"))||!t[i]||"a"!==new URL("https://a@b").username||"b"!==new URLSearchParams(new URLSearchParams("a=b")).get("a")||"xn--e1aybc"!==new URL("http://тест").host||"#%D0%B1"!==new URL("http://a#б").hash||"a1c3"!==n||"x"!==new URL("http://x",void 0).host}))},32302:(e,t,n)=>{var r=n(63405);e.exports=r&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},83937:(e,t,n)=>{var r=n(55746),o=n(95981);e.exports=r&&o((function(){return 42!=Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype}))},18348:e=>{var t=TypeError;e.exports=function(e,n){if(e{var r=n(21899),o=n(57475),a=r.WeakMap;e.exports=o(a)&&/native code/.test(String(a))},73464:(e,t,n)=>{var r=n(54058),o=n(90953),a=n(11477),i=n(65988).f;e.exports=function(e){var t=r.Symbol||(r.Symbol={});o(t,e)||i(t,e,{value:a.f(e)})}},11477:(e,t,n)=>{var r=n(99813);t.f=r},99813:(e,t,n)=>{var r=n(21899),o=n(68726),a=n(90953),i=n(99418),s=n(63405),l=n(32302),u=r.Symbol,c=o("wks"),p=l?u.for||u:u&&u.withoutSetter||i;e.exports=function(e){return a(c,e)||(c[e]=s&&a(u,e)?u[e]:p("Symbol."+e)),c[e]}},73483:e=>{e.exports="\t\n\v\f\r                 \u2028\u2029\ufeff"},49812:(e,t,n)=>{"use strict";var r=n(76887),o=n(7046),a=n(249),i=n(88929),s=n(23489),l=n(29290),u=n(32029),c=n(31887),p=n(53794),f=n(79585),h=n(93091),d=n(14649),m=n(99813)("toStringTag"),g=Error,y=[].push,v=function(e,t){var n,r=o(b,this);i?n=i(g(),r?a(this):b):(n=r?this:l(b),u(n,m,"Error")),void 0!==t&&u(n,"message",d(t)),f(n,v,n.stack,1),arguments.length>2&&p(n,arguments[2]);var s=[];return h(e,y,{that:s}),u(n,"errors",s),n};i?i(v,g):s(v,g,{name:!0});var b=v.prototype=l(g.prototype,{constructor:c(1,v),message:c(1,""),name:c(1,"AggregateError")});r({global:!0,constructor:!0,arity:2},{AggregateError:v})},47627:(e,t,n)=>{n(49812)},85906:(e,t,n)=>{"use strict";var r=n(76887),o=n(95981),a=n(1052),i=n(10941),s=n(89678),l=n(10623),u=n(66796),c=n(55449),p=n(64692),f=n(50568),h=n(99813),d=n(53385),m=h("isConcatSpreadable"),g=d>=51||!o((function(){var e=[];return e[m]=!1,e.concat()[0]!==e})),y=function(e){if(!i(e))return!1;var t=e[m];return void 0!==t?!!t:a(e)};r({target:"Array",proto:!0,arity:1,forced:!g||!f("concat")},{concat:function(e){var t,n,r,o,a,i=s(this),f=p(i,0),h=0;for(t=-1,r=arguments.length;t{"use strict";var r=n(76887),o=n(3610).every;r({target:"Array",proto:!0,forced:!n(34194)("every")},{every:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}})},80290:(e,t,n)=>{var r=n(76887),o=n(91860),a=n(18479);r({target:"Array",proto:!0},{fill:o}),a("fill")},21501:(e,t,n)=>{"use strict";var r=n(76887),o=n(3610).filter;r({target:"Array",proto:!0,forced:!n(50568)("filter")},{filter:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}})},44929:(e,t,n)=>{"use strict";var r=n(76887),o=n(3610).findIndex,a=n(18479),i="findIndex",s=!0;i in[]&&Array(1)[i]((function(){s=!1})),r({target:"Array",proto:!0,forced:s},{findIndex:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}}),a(i)},80833:(e,t,n)=>{"use strict";var r=n(76887),o=n(3610).find,a=n(18479),i="find",s=!0;i in[]&&Array(1)[i]((function(){s=!1})),r({target:"Array",proto:!0,forced:s},{find:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}}),a(i)},2437:(e,t,n)=>{"use strict";var r=n(76887),o=n(56837);r({target:"Array",proto:!0,forced:[].forEach!=o},{forEach:o})},53242:(e,t,n)=>{var r=n(76887),o=n(11354);r({target:"Array",stat:!0,forced:!n(21385)((function(e){Array.from(e)}))},{from:o})},97690:(e,t,n)=>{"use strict";var r=n(76887),o=n(31692).includes,a=n(95981),i=n(18479);r({target:"Array",proto:!0,forced:a((function(){return!Array(1).includes()}))},{includes:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}}),i("includes")},99076:(e,t,n)=>{"use strict";var r=n(76887),o=n(97484),a=n(31692).indexOf,i=n(34194),s=o([].indexOf),l=!!s&&1/s([1],1,-0)<0;r({target:"Array",proto:!0,forced:l||!i("indexOf")},{indexOf:function(e){var t=arguments.length>1?arguments[1]:void 0;return l?s(this,e,t)||0:a(this,e,t)}})},92737:(e,t,n)=>{n(76887)({target:"Array",stat:!0},{isArray:n(1052)})},66274:(e,t,n)=>{"use strict";var r=n(74529),o=n(18479),a=n(12077),i=n(45402),s=n(65988).f,l=n(75105),u=n(23538),c=n(82529),p=n(55746),f="Array Iterator",h=i.set,d=i.getterFor(f);e.exports=l(Array,"Array",(function(e,t){h(this,{type:f,target:r(e),index:0,kind:t})}),(function(){var e=d(this),t=e.target,n=e.kind,r=e.index++;return!t||r>=t.length?(e.target=void 0,u(void 0,!0)):u("keys"==n?r:"values"==n?t[r]:[r,t[r]],!1)}),"values");var m=a.Arguments=a.Array;if(o("keys"),o("values"),o("entries"),!c&&p&&"values"!==m.name)try{s(m,"name",{value:"values"})}catch(e){}},75915:(e,t,n)=>{var r=n(76887),o=n(67145);r({target:"Array",proto:!0,forced:o!==[].lastIndexOf},{lastIndexOf:o})},68787:(e,t,n)=>{"use strict";var r=n(76887),o=n(3610).map;r({target:"Array",proto:!0,forced:!n(50568)("map")},{map:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}})},81876:(e,t,n)=>{"use strict";var r=n(76887),o=n(46499).left,a=n(34194),i=n(53385);r({target:"Array",proto:!0,forced:!n(6049)&&i>79&&i<83||!a("reduce")},{reduce:function(e){var t=arguments.length;return o(this,e,t,t>1?arguments[1]:void 0)}})},60186:(e,t,n)=>{"use strict";var r=n(76887),o=n(1052),a=n(24284),i=n(10941),s=n(59413),l=n(10623),u=n(74529),c=n(55449),p=n(99813),f=n(50568),h=n(93765),d=f("slice"),m=p("species"),g=Array,y=Math.max;r({target:"Array",proto:!0,forced:!d},{slice:function(e,t){var n,r,p,f=u(this),d=l(f),v=s(e,d),b=s(void 0===t?d:t,d);if(o(f)&&(n=f.constructor,(a(n)&&(n===g||o(n.prototype))||i(n)&&null===(n=n[m]))&&(n=void 0),n===g||void 0===n))return h(f,v,b);for(r=new(void 0===n?g:n)(y(b-v,0)),p=0;v{"use strict";var r=n(76887),o=n(3610).some;r({target:"Array",proto:!0,forced:!n(34194)("some")},{some:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}})},4115:(e,t,n)=>{"use strict";var r=n(76887),o=n(95329),a=n(24883),i=n(89678),s=n(10623),l=n(15863),u=n(85803),c=n(95981),p=n(61388),f=n(34194),h=n(34342),d=n(81046),m=n(53385),g=n(18938),y=[],v=o(y.sort),b=o(y.push),w=c((function(){y.sort(void 0)})),E=c((function(){y.sort(null)})),x=f("sort"),_=!c((function(){if(m)return m<70;if(!(h&&h>3)){if(d)return!0;if(g)return g<603;var e,t,n,r,o="";for(e=65;e<76;e++){switch(t=String.fromCharCode(e),e){case 66:case 69:case 70:case 72:n=3;break;case 68:case 71:n=4;break;default:n=2}for(r=0;r<47;r++)y.push({k:t+r,v:n})}for(y.sort((function(e,t){return t.v-e.v})),r=0;ru(n)?1:-1}}(e)),n=s(o),r=0;r{"use strict";var r=n(76887),o=n(89678),a=n(59413),i=n(62435),s=n(10623),l=n(89779),u=n(66796),c=n(64692),p=n(55449),f=n(15863),h=n(50568)("splice"),d=Math.max,m=Math.min;r({target:"Array",proto:!0,forced:!h},{splice:function(e,t){var n,r,h,g,y,v,b=o(this),w=s(b),E=a(e,w),x=arguments.length;for(0===x?n=r=0:1===x?(n=0,r=w-E):(n=x-2,r=m(d(i(t),0),w-E)),u(w+n-r),h=c(b,r),g=0;gw-r+n;g--)f(b,g-1)}else if(n>r)for(g=w-r;g>E;g--)v=g+n-1,(y=g+r-1)in b?b[v]=b[y]:f(b,v);for(g=0;g{var r=n(76887),o=n(95329),a=Date,i=o(a.prototype.getTime);r({target:"Date",stat:!0},{now:function(){return i(new a)}})},18084:()=>{},73381:(e,t,n)=>{var r=n(76887),o=n(98308);r({target:"Function",proto:!0,forced:Function.bind!==o},{bind:o})},32619:(e,t,n)=>{var r=n(76887),o=n(626),a=n(79730),i=n(78834),s=n(95329),l=n(95981),u=n(57475),c=n(56664),p=n(93765),f=n(33323),h=n(63405),d=String,m=o("JSON","stringify"),g=s(/./.exec),y=s("".charAt),v=s("".charCodeAt),b=s("".replace),w=s(1..toString),E=/[\uD800-\uDFFF]/g,x=/^[\uD800-\uDBFF]$/,_=/^[\uDC00-\uDFFF]$/,S=!h||l((function(){var e=o("Symbol")();return"[null]"!=m([e])||"{}"!=m({a:e})||"{}"!=m(Object(e))})),A=l((function(){return'"\\udf06\\ud834"'!==m("\udf06\ud834")||'"\\udead"'!==m("\udead")})),C=function(e,t){var n=p(arguments),r=f(t);if(u(r)||void 0!==e&&!c(e))return n[1]=function(e,t){if(u(r)&&(t=i(r,this,d(e),t)),!c(t))return t},a(m,null,n)},k=function(e,t,n){var r=y(n,t-1),o=y(n,t+1);return g(x,e)&&!g(_,o)||g(_,e)&&!g(x,r)?"\\u"+w(v(e,0),16):e};m&&r({target:"JSON",stat:!0,arity:3,forced:S||A},{stringify:function(e,t,n){var r=p(arguments),o=a(S?C:m,null,r);return A&&"string"==typeof o?b(o,E,k):o}})},69120:(e,t,n)=>{var r=n(21899);n(90904)(r.JSON,"JSON",!0)},23112:(e,t,n)=>{"use strict";n(24683)("Map",(function(e){return function(){return e(this,arguments.length?arguments[0]:void 0)}}),n(85616))},37501:(e,t,n)=>{n(23112)},79413:()=>{},49221:(e,t,n)=>{var r=n(76887),o=n(24420);r({target:"Object",stat:!0,arity:2,forced:Object.assign!==o},{assign:o})},74979:(e,t,n)=>{var r=n(76887),o=n(55746),a=n(59938).f;r({target:"Object",stat:!0,forced:Object.defineProperties!==a,sham:!o},{defineProperties:a})},86450:(e,t,n)=>{var r=n(76887),o=n(55746),a=n(65988).f;r({target:"Object",stat:!0,forced:Object.defineProperty!==a,sham:!o},{defineProperty:a})},46924:(e,t,n)=>{var r=n(76887),o=n(95981),a=n(74529),i=n(49677).f,s=n(55746);r({target:"Object",stat:!0,forced:!s||o((function(){i(1)})),sham:!s},{getOwnPropertyDescriptor:function(e,t){return i(a(e),t)}})},88482:(e,t,n)=>{var r=n(76887),o=n(55746),a=n(31136),i=n(74529),s=n(49677),l=n(55449);r({target:"Object",stat:!0,sham:!o},{getOwnPropertyDescriptors:function(e){for(var t,n,r=i(e),o=s.f,u=a(r),c={},p=0;u.length>p;)void 0!==(n=o(r,t=u[p++]))&&l(c,t,n);return c}})},37144:(e,t,n)=>{var r=n(76887),o=n(63405),a=n(95981),i=n(87857),s=n(89678);r({target:"Object",stat:!0,forced:!o||a((function(){i.f(1)}))},{getOwnPropertySymbols:function(e){var t=i.f;return t?t(s(e)):[]}})},21724:(e,t,n)=>{var r=n(76887),o=n(89678),a=n(14771);r({target:"Object",stat:!0,forced:n(95981)((function(){a(1)}))},{keys:function(e){return a(o(e))}})},55967:()=>{},26614:(e,t,n)=>{var r=n(76887),o=n(88810).values;r({target:"Object",stat:!0},{values:function(e){return o(e)}})},4560:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),a=n(24883),i=n(69520),s=n(40002),l=n(93091);r({target:"Promise",stat:!0,forced:n(31542)},{allSettled:function(e){var t=this,n=i.f(t),r=n.resolve,u=n.reject,c=s((function(){var n=a(t.resolve),i=[],s=0,u=1;l(e,(function(e){var a=s++,l=!1;u++,o(n,t,e).then((function(e){l||(l=!0,i[a]={status:"fulfilled",value:e},--u||r(i))}),(function(e){l||(l=!0,i[a]={status:"rejected",reason:e},--u||r(i))}))})),--u||r(i)}));return c.error&&u(c.value),n.promise}})},16890:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),a=n(24883),i=n(69520),s=n(40002),l=n(93091);r({target:"Promise",stat:!0,forced:n(31542)},{all:function(e){var t=this,n=i.f(t),r=n.resolve,u=n.reject,c=s((function(){var n=a(t.resolve),i=[],s=0,c=1;l(e,(function(e){var a=s++,l=!1;c++,o(n,t,e).then((function(e){l||(l=!0,i[a]=e,--c||r(i))}),u)})),--c||r(i)}));return c.error&&u(c.value),n.promise}})},91302:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),a=n(24883),i=n(626),s=n(69520),l=n(40002),u=n(93091),c=n(31542),p="No one promise resolved";r({target:"Promise",stat:!0,forced:c},{any:function(e){var t=this,n=i("AggregateError"),r=s.f(t),c=r.resolve,f=r.reject,h=l((function(){var r=a(t.resolve),i=[],s=0,l=1,h=!1;u(e,(function(e){var a=s++,u=!1;l++,o(r,t,e).then((function(e){u||h||(h=!0,c(e))}),(function(e){u||h||(u=!0,i[a]=e,--l||f(new n(i,p)))}))})),--l||f(new n(i,p))}));return h.error&&f(h.value),r.promise}})},83376:(e,t,n)=>{"use strict";var r=n(76887),o=n(82529),a=n(67742).CONSTRUCTOR,i=n(6991),s=n(626),l=n(57475),u=n(95929),c=i&&i.prototype;if(r({target:"Promise",proto:!0,forced:a,real:!0},{catch:function(e){return this.then(void 0,e)}}),!o&&l(i)){var p=s("Promise").prototype.catch;c.catch!==p&&u(c,"catch",p,{unsafe:!0})}},26934:(e,t,n)=>{"use strict";var r,o,a,i=n(76887),s=n(82529),l=n(6049),u=n(21899),c=n(78834),p=n(95929),f=n(88929),h=n(90904),d=n(94431),m=n(24883),g=n(57475),y=n(10941),v=n(5743),b=n(70487),w=n(42941).set,E=n(66132),x=n(34845),_=n(40002),S=n(18397),A=n(45402),C=n(6991),k=n(67742),O=n(69520),j="Promise",T=k.CONSTRUCTOR,I=k.REJECTION_EVENT,N=k.SUBCLASSING,P=A.getterFor(j),R=A.set,M=C&&C.prototype,D=C,L=M,B=u.TypeError,F=u.document,U=u.process,z=O.f,q=z,$=!!(F&&F.createEvent&&u.dispatchEvent),V="unhandledrejection",H=function(e){var t;return!(!y(e)||!g(t=e.then))&&t},W=function(e,t){var n,r,o,a=t.value,i=1==t.state,s=i?e.ok:e.fail,l=e.resolve,u=e.reject,p=e.domain;try{s?(i||(2===t.rejection&&Y(t),t.rejection=1),!0===s?n=a:(p&&p.enter(),n=s(a),p&&(p.exit(),o=!0)),n===e.promise?u(B("Promise-chain cycle")):(r=H(n))?c(r,n,l,u):l(n)):u(a)}catch(e){p&&!o&&p.exit(),u(e)}},J=function(e,t){e.notified||(e.notified=!0,E((function(){for(var n,r=e.reactions;n=r.get();)W(n,e);e.notified=!1,t&&!e.rejection&&G(e)})))},K=function(e,t,n){var r,o;$?((r=F.createEvent("Event")).promise=t,r.reason=n,r.initEvent(e,!1,!0),u.dispatchEvent(r)):r={promise:t,reason:n},!I&&(o=u["on"+e])?o(r):e===V&&x("Unhandled promise rejection",n)},G=function(e){c(w,u,(function(){var t,n=e.facade,r=e.value;if(Z(e)&&(t=_((function(){l?U.emit("unhandledRejection",r,n):K(V,n,r)})),e.rejection=l||Z(e)?2:1,t.error))throw t.value}))},Z=function(e){return 1!==e.rejection&&!e.parent},Y=function(e){c(w,u,(function(){var t=e.facade;l?U.emit("rejectionHandled",t):K("rejectionhandled",t,e.value)}))},Q=function(e,t,n){return function(r){e(t,r,n)}},X=function(e,t,n){e.done||(e.done=!0,n&&(e=n),e.value=t,e.state=2,J(e,!0))},ee=function(e,t,n){if(!e.done){e.done=!0,n&&(e=n);try{if(e.facade===t)throw B("Promise can't be resolved itself");var r=H(t);r?E((function(){var n={done:!1};try{c(r,t,Q(ee,n,e),Q(X,n,e))}catch(t){X(n,t,e)}})):(e.value=t,e.state=1,J(e,!1))}catch(t){X({done:!1},t,e)}}};if(T&&(L=(D=function(e){v(this,L),m(e),c(r,this);var t=P(this);try{e(Q(ee,t),Q(X,t))}catch(e){X(t,e)}}).prototype,(r=function(e){R(this,{type:j,done:!1,notified:!1,parent:!1,reactions:new S,rejection:!1,state:0,value:void 0})}).prototype=p(L,"then",(function(e,t){var n=P(this),r=z(b(this,D));return n.parent=!0,r.ok=!g(e)||e,r.fail=g(t)&&t,r.domain=l?U.domain:void 0,0==n.state?n.reactions.add(r):E((function(){W(r,n)})),r.promise})),o=function(){var e=new r,t=P(e);this.promise=e,this.resolve=Q(ee,t),this.reject=Q(X,t)},O.f=z=function(e){return e===D||undefined===e?new o(e):q(e)},!s&&g(C)&&M!==Object.prototype)){a=M.then,N||p(M,"then",(function(e,t){var n=this;return new D((function(e,t){c(a,n,e,t)})).then(e,t)}),{unsafe:!0});try{delete M.constructor}catch(e){}f&&f(M,L)}i({global:!0,constructor:!0,wrap:!0,forced:T},{Promise:D}),h(D,j,!1,!0),d(j)},44349:(e,t,n)=>{"use strict";var r=n(76887),o=n(82529),a=n(6991),i=n(95981),s=n(626),l=n(57475),u=n(70487),c=n(56584),p=n(95929),f=a&&a.prototype;if(r({target:"Promise",proto:!0,real:!0,forced:!!a&&i((function(){f.finally.call({then:function(){}},(function(){}))}))},{finally:function(e){var t=u(this,s("Promise")),n=l(e);return this.then(n?function(n){return c(t,e()).then((function(){return n}))}:e,n?function(n){return c(t,e()).then((function(){throw n}))}:e)}}),!o&&l(a)){var h=s("Promise").prototype.finally;f.finally!==h&&p(f,"finally",h,{unsafe:!0})}},98881:(e,t,n)=>{n(26934),n(16890),n(83376),n(55921),n(64069),n(14482)},55921:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),a=n(24883),i=n(69520),s=n(40002),l=n(93091);r({target:"Promise",stat:!0,forced:n(31542)},{race:function(e){var t=this,n=i.f(t),r=n.reject,u=s((function(){var i=a(t.resolve);l(e,(function(e){o(i,t,e).then(n.resolve,r)}))}));return u.error&&r(u.value),n.promise}})},64069:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),a=n(69520);r({target:"Promise",stat:!0,forced:n(67742).CONSTRUCTOR},{reject:function(e){var t=a.f(this);return o(t.reject,void 0,e),t.promise}})},14482:(e,t,n)=>{"use strict";var r=n(76887),o=n(626),a=n(82529),i=n(6991),s=n(67742).CONSTRUCTOR,l=n(56584),u=o("Promise"),c=a&&!s;r({target:"Promise",stat:!0,forced:a||s},{resolve:function(e){return l(c&&this===u?i:this,e)}})},1502:()=>{},11035:(e,t,n)=>{"use strict";var r=n(76887),o=n(95329),a=n(70344),i=n(48219),s=n(85803),l=n(67772),u=o("".indexOf);r({target:"String",proto:!0,forced:!l("includes")},{includes:function(e){return!!~u(s(i(this)),s(a(e)),arguments.length>1?arguments[1]:void 0)}})},77971:(e,t,n)=>{"use strict";var r=n(64620).charAt,o=n(85803),a=n(45402),i=n(75105),s=n(23538),l="String Iterator",u=a.set,c=a.getterFor(l);i(String,"String",(function(e){u(this,{type:l,string:o(e),index:0})}),(function(){var e,t=c(this),n=t.string,o=t.index;return o>=n.length?s(void 0,!0):(e=r(n,o),t.index+=e.length,s(e,!1))}))},60986:(e,t,n)=>{n(76887)({target:"String",proto:!0},{repeat:n(16178)})},94761:(e,t,n)=>{"use strict";var r,o=n(76887),a=n(97484),i=n(49677).f,s=n(43057),l=n(85803),u=n(70344),c=n(48219),p=n(67772),f=n(82529),h=a("".startsWith),d=a("".slice),m=Math.min,g=p("startsWith");o({target:"String",proto:!0,forced:!!(f||g||(r=i(String.prototype,"startsWith"),!r||r.writable))&&!g},{startsWith:function(e){var t=l(c(this));u(e);var n=s(m(arguments.length>1?arguments[1]:void 0,t.length)),r=l(e);return h?h(t,r,n):d(t,n,n+r.length)===r}})},57398:(e,t,n)=>{"use strict";var r=n(76887),o=n(74853).trim;r({target:"String",proto:!0,forced:n(93093)("trim")},{trim:function(){return o(this)}})},8555:(e,t,n)=>{n(73464)("asyncIterator")},48616:(e,t,n)=>{"use strict";var r=n(76887),o=n(21899),a=n(78834),i=n(95329),s=n(82529),l=n(55746),u=n(63405),c=n(95981),p=n(90953),f=n(7046),h=n(96059),d=n(74529),m=n(83894),g=n(85803),y=n(31887),v=n(29290),b=n(14771),w=n(10946),E=n(684),x=n(87857),_=n(49677),S=n(65988),A=n(59938),C=n(36760),k=n(95929),O=n(29202),j=n(68726),T=n(44262),I=n(27748),N=n(99418),P=n(99813),R=n(11477),M=n(73464),D=n(29630),L=n(90904),B=n(45402),F=n(3610).forEach,U=T("hidden"),z="Symbol",q="prototype",$=B.set,V=B.getterFor(z),H=Object[q],W=o.Symbol,J=W&&W[q],K=o.TypeError,G=o.QObject,Z=_.f,Y=S.f,Q=E.f,X=C.f,ee=i([].push),te=j("symbols"),ne=j("op-symbols"),re=j("wks"),oe=!G||!G[q]||!G[q].findChild,ae=l&&c((function(){return 7!=v(Y({},"a",{get:function(){return Y(this,"a",{value:7}).a}})).a}))?function(e,t,n){var r=Z(H,t);r&&delete H[t],Y(e,t,n),r&&e!==H&&Y(H,t,r)}:Y,ie=function(e,t){var n=te[e]=v(J);return $(n,{type:z,tag:e,description:t}),l||(n.description=t),n},se=function(e,t,n){e===H&&se(ne,t,n),h(e);var r=m(t);return h(n),p(te,r)?(n.enumerable?(p(e,U)&&e[U][r]&&(e[U][r]=!1),n=v(n,{enumerable:y(0,!1)})):(p(e,U)||Y(e,U,y(1,{})),e[U][r]=!0),ae(e,r,n)):Y(e,r,n)},le=function(e,t){h(e);var n=d(t),r=b(n).concat(fe(n));return F(r,(function(t){l&&!a(ue,n,t)||se(e,t,n[t])})),e},ue=function(e){var t=m(e),n=a(X,this,t);return!(this===H&&p(te,t)&&!p(ne,t))&&(!(n||!p(this,t)||!p(te,t)||p(this,U)&&this[U][t])||n)},ce=function(e,t){var n=d(e),r=m(t);if(n!==H||!p(te,r)||p(ne,r)){var o=Z(n,r);return!o||!p(te,r)||p(n,U)&&n[U][r]||(o.enumerable=!0),o}},pe=function(e){var t=Q(d(e)),n=[];return F(t,(function(e){p(te,e)||p(I,e)||ee(n,e)})),n},fe=function(e){var t=e===H,n=Q(t?ne:d(e)),r=[];return F(n,(function(e){!p(te,e)||t&&!p(H,e)||ee(r,te[e])})),r};u||(k(J=(W=function(){if(f(J,this))throw K("Symbol is not a constructor");var e=arguments.length&&void 0!==arguments[0]?g(arguments[0]):void 0,t=N(e),n=function(e){this===H&&a(n,ne,e),p(this,U)&&p(this[U],t)&&(this[U][t]=!1),ae(this,t,y(1,e))};return l&&oe&&ae(H,t,{configurable:!0,set:n}),ie(t,e)})[q],"toString",(function(){return V(this).tag})),k(W,"withoutSetter",(function(e){return ie(N(e),e)})),C.f=ue,S.f=se,A.f=le,_.f=ce,w.f=E.f=pe,x.f=fe,R.f=function(e){return ie(P(e),e)},l&&(O(J,"description",{configurable:!0,get:function(){return V(this).description}}),s||k(H,"propertyIsEnumerable",ue,{unsafe:!0}))),r({global:!0,constructor:!0,wrap:!0,forced:!u,sham:!u},{Symbol:W}),F(b(re),(function(e){M(e)})),r({target:z,stat:!0,forced:!u},{useSetter:function(){oe=!0},useSimple:function(){oe=!1}}),r({target:"Object",stat:!0,forced:!u,sham:!l},{create:function(e,t){return void 0===t?v(e):le(v(e),t)},defineProperty:se,defineProperties:le,getOwnPropertyDescriptor:ce}),r({target:"Object",stat:!0,forced:!u},{getOwnPropertyNames:pe}),D(),L(W,z),I[U]=!0},52615:()=>{},64523:(e,t,n)=>{var r=n(76887),o=n(626),a=n(90953),i=n(85803),s=n(68726),l=n(34680),u=s("string-to-symbol-registry"),c=s("symbol-to-string-registry");r({target:"Symbol",stat:!0,forced:!l},{for:function(e){var t=i(e);if(a(u,t))return u[t];var n=o("Symbol")(t);return u[t]=n,c[n]=t,n}})},21732:(e,t,n)=>{n(73464)("hasInstance")},35903:(e,t,n)=>{n(73464)("isConcatSpreadable")},1825:(e,t,n)=>{n(73464)("iterator")},35824:(e,t,n)=>{n(48616),n(64523),n(38608),n(32619),n(37144)},38608:(e,t,n)=>{var r=n(76887),o=n(90953),a=n(56664),i=n(69826),s=n(68726),l=n(34680),u=s("symbol-to-string-registry");r({target:"Symbol",stat:!0,forced:!l},{keyFor:function(e){if(!a(e))throw TypeError(i(e)+" is not a symbol");if(o(u,e))return u[e]}})},45915:(e,t,n)=>{n(73464)("matchAll")},28394:(e,t,n)=>{n(73464)("match")},61766:(e,t,n)=>{n(73464)("replace")},62737:(e,t,n)=>{n(73464)("search")},89911:(e,t,n)=>{n(73464)("species")},74315:(e,t,n)=>{n(73464)("split")},63131:(e,t,n)=>{var r=n(73464),o=n(29630);r("toPrimitive"),o()},64714:(e,t,n)=>{var r=n(626),o=n(73464),a=n(90904);o("toStringTag"),a(r("Symbol"),"Symbol")},70659:(e,t,n)=>{n(73464)("unscopables")},28783:(e,t,n)=>{n(73464)("asyncDispose")},43975:(e,t,n)=>{n(73464)("dispose")},22731:(e,t,n)=>{var r=n(76887),o=n(626),a=n(95329),i=o("Symbol"),s=i.keyFor,l=a(i.prototype.valueOf);r({target:"Symbol",stat:!0},{isRegistered:function(e){try{return void 0!==s(l(e))}catch(e){return!1}}})},85605:(e,t,n)=>{for(var r=n(76887),o=n(68726),a=n(626),i=n(95329),s=n(56664),l=n(99813),u=a("Symbol"),c=u.isWellKnown,p=a("Object","getOwnPropertyNames"),f=i(u.prototype.valueOf),h=o("wks"),d=0,m=p(u),g=m.length;d{n(73464)("matcher")},31943:(e,t,n)=>{n(73464)("metadataKey")},45414:(e,t,n)=>{n(73464)("metadata")},46774:(e,t,n)=>{n(73464)("observable")},80620:(e,t,n)=>{n(73464)("patternMatch")},36172:(e,t,n)=>{n(73464)("replaceAll")},7634:(e,t,n)=>{n(66274);var r=n(63281),o=n(21899),a=n(9697),i=n(32029),s=n(12077),l=n(99813)("toStringTag");for(var u in r){var c=o[u],p=c&&c.prototype;p&&a(p)!==l&&i(p,l,u),s[u]=s.Array}},79229:(e,t,n)=>{var r=n(76887),o=n(21899),a=n(37620)(o.setInterval,!0);r({global:!0,bind:!0,forced:o.setInterval!==a},{setInterval:a})},17749:(e,t,n)=>{var r=n(76887),o=n(21899),a=n(37620)(o.setTimeout,!0);r({global:!0,bind:!0,forced:o.setTimeout!==a},{setTimeout:a})},71249:(e,t,n)=>{n(79229),n(17749)},62524:(e,t,n)=>{"use strict";n(66274);var r=n(76887),o=n(21899),a=n(78834),i=n(95329),s=n(55746),l=n(14766),u=n(95929),c=n(94380),p=n(90904),f=n(53847),h=n(45402),d=n(5743),m=n(57475),g=n(90953),y=n(86843),v=n(9697),b=n(96059),w=n(10941),E=n(85803),x=n(29290),_=n(31887),S=n(53476),A=n(22902),C=n(18348),k=n(99813),O=n(61388),j=k("iterator"),T="URLSearchParams",I=T+"Iterator",N=h.set,P=h.getterFor(T),R=h.getterFor(I),M=Object.getOwnPropertyDescriptor,D=function(e){if(!s)return o[e];var t=M(o,e);return t&&t.value},L=D("fetch"),B=D("Request"),F=D("Headers"),U=B&&B.prototype,z=F&&F.prototype,q=o.RegExp,$=o.TypeError,V=o.decodeURIComponent,H=o.encodeURIComponent,W=i("".charAt),J=i([].join),K=i([].push),G=i("".replace),Z=i([].shift),Y=i([].splice),Q=i("".split),X=i("".slice),ee=/\+/g,te=Array(4),ne=function(e){return te[e-1]||(te[e-1]=q("((?:%[\\da-f]{2}){"+e+"})","gi"))},re=function(e){try{return V(e)}catch(t){return e}},oe=function(e){var t=G(e,ee," "),n=4;try{return V(t)}catch(e){for(;n;)t=G(t,ne(n--),re);return t}},ae=/[!'()~]|%20/g,ie={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+"},se=function(e){return ie[e]},le=function(e){return G(H(e),ae,se)},ue=f((function(e,t){N(this,{type:I,iterator:S(P(e).entries),kind:t})}),"Iterator",(function(){var e=R(this),t=e.kind,n=e.iterator.next(),r=n.value;return n.done||(n.value="keys"===t?r.key:"values"===t?r.value:[r.key,r.value]),n}),!0),ce=function(e){this.entries=[],this.url=null,void 0!==e&&(w(e)?this.parseObject(e):this.parseQuery("string"==typeof e?"?"===W(e,0)?X(e,1):e:E(e)))};ce.prototype={type:T,bindURL:function(e){this.url=e,this.update()},parseObject:function(e){var t,n,r,o,i,s,l,u=A(e);if(u)for(n=(t=S(e,u)).next;!(r=a(n,t)).done;){if(i=(o=S(b(r.value))).next,(s=a(i,o)).done||(l=a(i,o)).done||!a(i,o).done)throw $("Expected sequence with length 2");K(this.entries,{key:E(s.value),value:E(l.value)})}else for(var c in e)g(e,c)&&K(this.entries,{key:c,value:E(e[c])})},parseQuery:function(e){if(e)for(var t,n,r=Q(e,"&"),o=0;o0?arguments[0]:void 0))},fe=pe.prototype;if(c(fe,{append:function(e,t){C(arguments.length,2);var n=P(this);K(n.entries,{key:E(e),value:E(t)}),n.updateURL()},delete:function(e){C(arguments.length,1);for(var t=P(this),n=t.entries,r=E(e),o=0;ot.key?1:-1})),e.updateURL()},forEach:function(e){for(var t,n=P(this).entries,r=y(e,arguments.length>1?arguments[1]:void 0),o=0;o1?me(arguments[1]):{})}}),m(B)){var ge=function(e){return d(this,U),new B(e,arguments.length>1?me(arguments[1]):{})};U.constructor=ge,ge.prototype=U,r({global:!0,constructor:!0,dontCallGetSet:!0,forced:!0},{Request:ge})}}e.exports={URLSearchParams:pe,getState:P}},95304:(e,t,n)=>{n(62524)},47250:(e,t,n)=>{"use strict";n(77971);var r,o=n(76887),a=n(55746),i=n(14766),s=n(21899),l=n(86843),u=n(95329),c=n(95929),p=n(29202),f=n(5743),h=n(90953),d=n(24420),m=n(11354),g=n(15790),y=n(64620).codeAt,v=n(73291),b=n(85803),w=n(90904),E=n(18348),x=n(62524),_=n(45402),S=_.set,A=_.getterFor("URL"),C=x.URLSearchParams,k=x.getState,O=s.URL,j=s.TypeError,T=s.parseInt,I=Math.floor,N=Math.pow,P=u("".charAt),R=u(/./.exec),M=u([].join),D=u(1..toString),L=u([].pop),B=u([].push),F=u("".replace),U=u([].shift),z=u("".split),q=u("".slice),$=u("".toLowerCase),V=u([].unshift),H="Invalid scheme",W="Invalid host",J="Invalid port",K=/[a-z]/i,G=/[\d+-.a-z]/i,Z=/\d/,Y=/^0x/i,Q=/^[0-7]+$/,X=/^\d+$/,ee=/^[\da-f]+$/i,te=/[\0\t\n\r #%/:<>?@[\\\]^|]/,ne=/[\0\t\n\r #/:<>?@[\\\]^|]/,re=/^[\u0000-\u0020]+/,oe=/(^|[^\u0000-\u0020])[\u0000-\u0020]+$/,ae=/[\t\n\r]/g,ie=function(e){var t,n,r,o;if("number"==typeof e){for(t=[],n=0;n<4;n++)V(t,e%256),e=I(e/256);return M(t,".")}if("object"==typeof e){for(t="",r=function(e){for(var t=null,n=1,r=null,o=0,a=0;a<8;a++)0!==e[a]?(o>n&&(t=r,n=o),r=null,o=0):(null===r&&(r=a),++o);return o>n&&(t=r,n=o),t}(e),n=0;n<8;n++)o&&0===e[n]||(o&&(o=!1),r===n?(t+=n?":":"::",o=!0):(t+=D(e[n],16),n<7&&(t+=":")));return"["+t+"]"}return e},se={},le=d({},se,{" ":1,'"':1,"<":1,">":1,"`":1}),ue=d({},le,{"#":1,"?":1,"{":1,"}":1}),ce=d({},ue,{"/":1,":":1,";":1,"=":1,"@":1,"[":1,"\\":1,"]":1,"^":1,"|":1}),pe=function(e,t){var n=y(e,0);return n>32&&n<127&&!h(t,e)?e:encodeURIComponent(e)},fe={ftp:21,file:null,http:80,https:443,ws:80,wss:443},he=function(e,t){var n;return 2==e.length&&R(K,P(e,0))&&(":"==(n=P(e,1))||!t&&"|"==n)},de=function(e){var t;return e.length>1&&he(q(e,0,2))&&(2==e.length||"/"===(t=P(e,2))||"\\"===t||"?"===t||"#"===t)},me=function(e){return"."===e||"%2e"===$(e)},ge={},ye={},ve={},be={},we={},Ee={},xe={},_e={},Se={},Ae={},Ce={},ke={},Oe={},je={},Te={},Ie={},Ne={},Pe={},Re={},Me={},De={},Le=function(e,t,n){var r,o,a,i=b(e);if(t){if(o=this.parse(i))throw j(o);this.searchParams=null}else{if(void 0!==n&&(r=new Le(n,!0)),o=this.parse(i,null,r))throw j(o);(a=k(new C)).bindURL(this),this.searchParams=a}};Le.prototype={type:"URL",parse:function(e,t,n){var o,a,i,s,l,u=this,c=t||ge,p=0,f="",d=!1,y=!1,v=!1;for(e=b(e),t||(u.scheme="",u.username="",u.password="",u.host=null,u.port=null,u.path=[],u.query=null,u.fragment=null,u.cannotBeABaseURL=!1,e=F(e,re,""),e=F(e,oe,"$1")),e=F(e,ae,""),o=m(e);p<=o.length;){switch(a=o[p],c){case ge:if(!a||!R(K,a)){if(t)return H;c=ve;continue}f+=$(a),c=ye;break;case ye:if(a&&(R(G,a)||"+"==a||"-"==a||"."==a))f+=$(a);else{if(":"!=a){if(t)return H;f="",c=ve,p=0;continue}if(t&&(u.isSpecial()!=h(fe,f)||"file"==f&&(u.includesCredentials()||null!==u.port)||"file"==u.scheme&&!u.host))return;if(u.scheme=f,t)return void(u.isSpecial()&&fe[u.scheme]==u.port&&(u.port=null));f="","file"==u.scheme?c=je:u.isSpecial()&&n&&n.scheme==u.scheme?c=be:u.isSpecial()?c=_e:"/"==o[p+1]?(c=we,p++):(u.cannotBeABaseURL=!0,B(u.path,""),c=Re)}break;case ve:if(!n||n.cannotBeABaseURL&&"#"!=a)return H;if(n.cannotBeABaseURL&&"#"==a){u.scheme=n.scheme,u.path=g(n.path),u.query=n.query,u.fragment="",u.cannotBeABaseURL=!0,c=De;break}c="file"==n.scheme?je:Ee;continue;case be:if("/"!=a||"/"!=o[p+1]){c=Ee;continue}c=Se,p++;break;case we:if("/"==a){c=Ae;break}c=Pe;continue;case Ee:if(u.scheme=n.scheme,a==r)u.username=n.username,u.password=n.password,u.host=n.host,u.port=n.port,u.path=g(n.path),u.query=n.query;else if("/"==a||"\\"==a&&u.isSpecial())c=xe;else if("?"==a)u.username=n.username,u.password=n.password,u.host=n.host,u.port=n.port,u.path=g(n.path),u.query="",c=Me;else{if("#"!=a){u.username=n.username,u.password=n.password,u.host=n.host,u.port=n.port,u.path=g(n.path),u.path.length--,c=Pe;continue}u.username=n.username,u.password=n.password,u.host=n.host,u.port=n.port,u.path=g(n.path),u.query=n.query,u.fragment="",c=De}break;case xe:if(!u.isSpecial()||"/"!=a&&"\\"!=a){if("/"!=a){u.username=n.username,u.password=n.password,u.host=n.host,u.port=n.port,c=Pe;continue}c=Ae}else c=Se;break;case _e:if(c=Se,"/"!=a||"/"!=P(f,p+1))continue;p++;break;case Se:if("/"!=a&&"\\"!=a){c=Ae;continue}break;case Ae:if("@"==a){d&&(f="%40"+f),d=!0,i=m(f);for(var w=0;w65535)return J;u.port=u.isSpecial()&&_===fe[u.scheme]?null:_,f=""}if(t)return;c=Ne;continue}return J}f+=a;break;case je:if(u.scheme="file","/"==a||"\\"==a)c=Te;else{if(!n||"file"!=n.scheme){c=Pe;continue}if(a==r)u.host=n.host,u.path=g(n.path),u.query=n.query;else if("?"==a)u.host=n.host,u.path=g(n.path),u.query="",c=Me;else{if("#"!=a){de(M(g(o,p),""))||(u.host=n.host,u.path=g(n.path),u.shortenPath()),c=Pe;continue}u.host=n.host,u.path=g(n.path),u.query=n.query,u.fragment="",c=De}}break;case Te:if("/"==a||"\\"==a){c=Ie;break}n&&"file"==n.scheme&&!de(M(g(o,p),""))&&(he(n.path[0],!0)?B(u.path,n.path[0]):u.host=n.host),c=Pe;continue;case Ie:if(a==r||"/"==a||"\\"==a||"?"==a||"#"==a){if(!t&&he(f))c=Pe;else if(""==f){if(u.host="",t)return;c=Ne}else{if(s=u.parseHost(f))return s;if("localhost"==u.host&&(u.host=""),t)return;f="",c=Ne}continue}f+=a;break;case Ne:if(u.isSpecial()){if(c=Pe,"/"!=a&&"\\"!=a)continue}else if(t||"?"!=a)if(t||"#"!=a){if(a!=r&&(c=Pe,"/"!=a))continue}else u.fragment="",c=De;else u.query="",c=Me;break;case Pe:if(a==r||"/"==a||"\\"==a&&u.isSpecial()||!t&&("?"==a||"#"==a)){if(".."===(l=$(l=f))||"%2e."===l||".%2e"===l||"%2e%2e"===l?(u.shortenPath(),"/"==a||"\\"==a&&u.isSpecial()||B(u.path,"")):me(f)?"/"==a||"\\"==a&&u.isSpecial()||B(u.path,""):("file"==u.scheme&&!u.path.length&&he(f)&&(u.host&&(u.host=""),f=P(f,0)+":"),B(u.path,f)),f="","file"==u.scheme&&(a==r||"?"==a||"#"==a))for(;u.path.length>1&&""===u.path[0];)U(u.path);"?"==a?(u.query="",c=Me):"#"==a&&(u.fragment="",c=De)}else f+=pe(a,ue);break;case Re:"?"==a?(u.query="",c=Me):"#"==a?(u.fragment="",c=De):a!=r&&(u.path[0]+=pe(a,se));break;case Me:t||"#"!=a?a!=r&&("'"==a&&u.isSpecial()?u.query+="%27":u.query+="#"==a?"%23":pe(a,se)):(u.fragment="",c=De);break;case De:a!=r&&(u.fragment+=pe(a,le))}p++}},parseHost:function(e){var t,n,r;if("["==P(e,0)){if("]"!=P(e,e.length-1))return W;if(t=function(e){var t,n,r,o,a,i,s,l=[0,0,0,0,0,0,0,0],u=0,c=null,p=0,f=function(){return P(e,p)};if(":"==f()){if(":"!=P(e,1))return;p+=2,c=++u}for(;f();){if(8==u)return;if(":"!=f()){for(t=n=0;n<4&&R(ee,f());)t=16*t+T(f(),16),p++,n++;if("."==f()){if(0==n)return;if(p-=n,u>6)return;for(r=0;f();){if(o=null,r>0){if(!("."==f()&&r<4))return;p++}if(!R(Z,f()))return;for(;R(Z,f());){if(a=T(f(),10),null===o)o=a;else{if(0==o)return;o=10*o+a}if(o>255)return;p++}l[u]=256*l[u]+o,2!=++r&&4!=r||u++}if(4!=r)return;break}if(":"==f()){if(p++,!f())return}else if(f())return;l[u++]=t}else{if(null!==c)return;p++,c=++u}}if(null!==c)for(i=u-c,u=7;0!=u&&i>0;)s=l[u],l[u--]=l[c+i-1],l[c+--i]=s;else if(8!=u)return;return l}(q(e,1,-1)),!t)return W;this.host=t}else if(this.isSpecial()){if(e=v(e),R(te,e))return W;if(t=function(e){var t,n,r,o,a,i,s,l=z(e,".");if(l.length&&""==l[l.length-1]&&l.length--,(t=l.length)>4)return e;for(n=[],r=0;r1&&"0"==P(o,0)&&(a=R(Y,o)?16:8,o=q(o,8==a?1:2)),""===o)i=0;else{if(!R(10==a?X:8==a?Q:ee,o))return e;i=T(o,a)}B(n,i)}for(r=0;r=N(256,5-t))return null}else if(i>255)return null;for(s=L(n),r=0;r1?arguments[1]:void 0,r=S(t,new Le(e,!1,n));a||(t.href=r.serialize(),t.origin=r.getOrigin(),t.protocol=r.getProtocol(),t.username=r.getUsername(),t.password=r.getPassword(),t.host=r.getHost(),t.hostname=r.getHostname(),t.port=r.getPort(),t.pathname=r.getPathname(),t.search=r.getSearch(),t.searchParams=r.getSearchParams(),t.hash=r.getHash())},Fe=Be.prototype,Ue=function(e,t){return{get:function(){return A(this)[e]()},set:t&&function(e){return A(this)[t](e)},configurable:!0,enumerable:!0}};if(a&&(p(Fe,"href",Ue("serialize","setHref")),p(Fe,"origin",Ue("getOrigin")),p(Fe,"protocol",Ue("getProtocol","setProtocol")),p(Fe,"username",Ue("getUsername","setUsername")),p(Fe,"password",Ue("getPassword","setPassword")),p(Fe,"host",Ue("getHost","setHost")),p(Fe,"hostname",Ue("getHostname","setHostname")),p(Fe,"port",Ue("getPort","setPort")),p(Fe,"pathname",Ue("getPathname","setPathname")),p(Fe,"search",Ue("getSearch","setSearch")),p(Fe,"searchParams",Ue("getSearchParams")),p(Fe,"hash",Ue("getHash","setHash"))),c(Fe,"toJSON",(function(){return A(this).serialize()}),{enumerable:!0}),c(Fe,"toString",(function(){return A(this).serialize()}),{enumerable:!0}),O){var ze=O.createObjectURL,qe=O.revokeObjectURL;ze&&c(Be,"createObjectURL",l(ze,O)),qe&&c(Be,"revokeObjectURL",l(qe,O))}w(Be,"URL"),o({global:!0,constructor:!0,forced:!i,sham:!a},{URL:Be})},33601:(e,t,n)=>{n(47250)},98947:()=>{},24848:(e,t,n)=>{var r=n(54493);e.exports=r},83363:(e,t,n)=>{var r=n(24034);e.exports=r},62908:(e,t,n)=>{var r=n(12710);e.exports=r},49216:(e,t,n)=>{var r=n(99324);e.exports=r},56668:(e,t,n)=>{var r=n(95909);e.exports=r},74719:(e,t,n)=>{var r=n(14423);e.exports=r},57784:(e,t,n)=>{var r=n(81103);e.exports=r},28196:(e,t,n)=>{var r=n(16246);e.exports=r},8065:(e,t,n)=>{var r=n(56043);e.exports=r},57448:(e,t,n)=>{n(7634);var r=n(9697),o=n(90953),a=n(7046),i=n(62908),s=Array.prototype,l={DOMTokenList:!0,NodeList:!0};e.exports=function(e){var t=e.entries;return e===s||a(s,e)&&t===s.entries||o(l,r(e))?i:t}},29455:(e,t,n)=>{var r=n(13160);e.exports=r},69743:(e,t,n)=>{var r=n(80446);e.exports=r},11955:(e,t,n)=>{var r=n(2480);e.exports=r},96064:(e,t,n)=>{var r=n(7147);e.exports=r},61577:(e,t,n)=>{var r=n(32236);e.exports=r},46279:(e,t,n)=>{n(7634);var r=n(9697),o=n(90953),a=n(7046),i=n(49216),s=Array.prototype,l={DOMTokenList:!0,NodeList:!0};e.exports=function(e){var t=e.forEach;return e===s||a(s,e)&&t===s.forEach||o(l,r(e))?i:t}},33778:(e,t,n)=>{var r=n(58557);e.exports=r},19373:(e,t,n)=>{var r=n(34570);e.exports=r},73819:(e,t,n)=>{n(7634);var r=n(9697),o=n(90953),a=n(7046),i=n(56668),s=Array.prototype,l={DOMTokenList:!0,NodeList:!0};e.exports=function(e){var t=e.keys;return e===s||a(s,e)&&t===s.keys||o(l,r(e))?i:t}},11022:(e,t,n)=>{var r=n(57564);e.exports=r},61798:(e,t,n)=>{var r=n(88287);e.exports=r},52527:(e,t,n)=>{var r=n(68025);e.exports=r},36857:(e,t,n)=>{var r=n(59257);e.exports=r},82073:(e,t,n)=>{var r=n(69601);e.exports=r},45286:(e,t,n)=>{var r=n(28299);e.exports=r},62856:(e,t,n)=>{var r=n(69355);e.exports=r},2348:(e,t,n)=>{var r=n(18339);e.exports=r},35178:(e,t,n)=>{var r=n(71611);e.exports=r},76361:(e,t,n)=>{var r=n(62774);e.exports=r},71815:(e,t,n)=>{n(7634);var r=n(9697),o=n(90953),a=n(7046),i=n(74719),s=Array.prototype,l={DOMTokenList:!0,NodeList:!0};e.exports=function(e){var t=e.values;return e===s||a(s,e)&&t===s.values||o(l,r(e))?i:t}},8933:(e,t,n)=>{var r=n(84426);e.exports=r},15868:(e,t,n)=>{var r=n(91018);n(7634),e.exports=r},63383:(e,t,n)=>{var r=n(45999);e.exports=r},57396:(e,t,n)=>{var r=n(7702);e.exports=r},41910:(e,t,n)=>{var r=n(48171);e.exports=r},79427:(e,t,n)=>{var r=n(286);e.exports=r},62857:(e,t,n)=>{var r=n(92766);e.exports=r},9534:(e,t,n)=>{var r=n(30498);e.exports=r},23059:(e,t,n)=>{var r=n(48494);e.exports=r},47795:(e,t,n)=>{var r=n(98430);e.exports=r},27460:(e,t,n)=>{var r=n(52956);n(7634),e.exports=r},27989:(e,t,n)=>{n(71249);var r=n(54058);e.exports=r.setTimeout},92547:(e,t,n)=>{var r=n(57473);n(7634),e.exports=r},46509:(e,t,n)=>{var r=n(24227);n(7634),e.exports=r},35774:(e,t,n)=>{var r=n(62978);e.exports=r},57641:(e,t,n)=>{var r=n(71459);e.exports=r},71459:(e,t,n)=>{n(33601),n(98947),n(95304);var r=n(54058);e.exports=r.URL},31905:function(){!function(e){!function(t){var n={searchParams:"URLSearchParams"in e,iterable:"Symbol"in e&&"iterator"in Symbol,blob:"FileReader"in e&&"Blob"in e&&function(){try{return new Blob,!0}catch(e){return!1}}(),formData:"FormData"in e,arrayBuffer:"ArrayBuffer"in e};if(n.arrayBuffer)var r=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],o=ArrayBuffer.isView||function(e){return e&&r.indexOf(Object.prototype.toString.call(e))>-1};function a(e){if("string"!=typeof e&&(e=String(e)),/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(e))throw new TypeError("Invalid character in header field name");return e.toLowerCase()}function i(e){return"string"!=typeof e&&(e=String(e)),e}function s(e){var t={next:function(){var t=e.shift();return{done:void 0===t,value:t}}};return n.iterable&&(t[Symbol.iterator]=function(){return t}),t}function l(e){this.map={},e instanceof l?e.forEach((function(e,t){this.append(t,e)}),this):Array.isArray(e)?e.forEach((function(e){this.append(e[0],e[1])}),this):e&&Object.getOwnPropertyNames(e).forEach((function(t){this.append(t,e[t])}),this)}function u(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function c(e){return new Promise((function(t,n){e.onload=function(){t(e.result)},e.onerror=function(){n(e.error)}}))}function p(e){var t=new FileReader,n=c(t);return t.readAsArrayBuffer(e),n}function f(e){if(e.slice)return e.slice(0);var t=new Uint8Array(e.byteLength);return t.set(new Uint8Array(e)),t.buffer}function h(){return this.bodyUsed=!1,this._initBody=function(e){var t;this._bodyInit=e,e?"string"==typeof e?this._bodyText=e:n.blob&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:n.formData&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:n.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():n.arrayBuffer&&n.blob&&((t=e)&&DataView.prototype.isPrototypeOf(t))?(this._bodyArrayBuffer=f(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):n.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(e)||o(e))?this._bodyArrayBuffer=f(e):this._bodyText=e=Object.prototype.toString.call(e):this._bodyText="",this.headers.get("content-type")||("string"==typeof e?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):n.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},n.blob&&(this.blob=function(){var e=u(this);if(e)return e;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this._bodyArrayBuffer?u(this)||Promise.resolve(this._bodyArrayBuffer):this.blob().then(p)}),this.text=function(){var e,t,n,r=u(this);if(r)return r;if(this._bodyBlob)return e=this._bodyBlob,t=new FileReader,n=c(t),t.readAsText(e),n;if(this._bodyArrayBuffer)return Promise.resolve(function(e){for(var t=new Uint8Array(e),n=new Array(t.length),r=0;r-1?r:n),this.mode=t.mode||this.mode||null,this.signal=t.signal||this.signal,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&o)throw new TypeError("Body not allowed for GET or HEAD requests");this._initBody(o)}function g(e){var t=new FormData;return e.trim().split("&").forEach((function(e){if(e){var n=e.split("="),r=n.shift().replace(/\+/g," "),o=n.join("=").replace(/\+/g," ");t.append(decodeURIComponent(r),decodeURIComponent(o))}})),t}function y(e,t){t||(t={}),this.type="default",this.status=void 0===t.status?200:t.status,this.ok=this.status>=200&&this.status<300,this.statusText="statusText"in t?t.statusText:"OK",this.headers=new l(t.headers),this.url=t.url||"",this._initBody(e)}m.prototype.clone=function(){return new m(this,{body:this._bodyInit})},h.call(m.prototype),h.call(y.prototype),y.prototype.clone=function(){return new y(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new l(this.headers),url:this.url})},y.error=function(){var e=new y(null,{status:0,statusText:""});return e.type="error",e};var v=[301,302,303,307,308];y.redirect=function(e,t){if(-1===v.indexOf(t))throw new RangeError("Invalid status code");return new y(null,{status:t,headers:{location:e}})},t.DOMException=e.DOMException;try{new t.DOMException}catch(e){t.DOMException=function(e,t){this.message=e,this.name=t;var n=Error(e);this.stack=n.stack},t.DOMException.prototype=Object.create(Error.prototype),t.DOMException.prototype.constructor=t.DOMException}function b(e,r){return new Promise((function(o,a){var i=new m(e,r);if(i.signal&&i.signal.aborted)return a(new t.DOMException("Aborted","AbortError"));var s=new XMLHttpRequest;function u(){s.abort()}s.onload=function(){var e,t,n={status:s.status,statusText:s.statusText,headers:(e=s.getAllResponseHeaders()||"",t=new l,e.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach((function(e){var n=e.split(":"),r=n.shift().trim();if(r){var o=n.join(":").trim();t.append(r,o)}})),t)};n.url="responseURL"in s?s.responseURL:n.headers.get("X-Request-URL");var r="response"in s?s.response:s.responseText;o(new y(r,n))},s.onerror=function(){a(new TypeError("Network request failed"))},s.ontimeout=function(){a(new TypeError("Network request failed"))},s.onabort=function(){a(new t.DOMException("Aborted","AbortError"))},s.open(i.method,i.url,!0),"include"===i.credentials?s.withCredentials=!0:"omit"===i.credentials&&(s.withCredentials=!1),"responseType"in s&&n.blob&&(s.responseType="blob"),i.headers.forEach((function(e,t){s.setRequestHeader(t,e)})),i.signal&&(i.signal.addEventListener("abort",u),s.onreadystatechange=function(){4===s.readyState&&i.signal.removeEventListener("abort",u)}),s.send(void 0===i._bodyInit?null:i._bodyInit)}))}b.polyfill=!0,e.fetch||(e.fetch=b,e.Headers=l,e.Request=m,e.Response=y),t.Headers=l,t.Request=m,t.Response=y,t.fetch=b,Object.defineProperty(t,"__esModule",{value:!0})}({})}("undefined"!=typeof self?self:this)},8269:function(e,t,n){var r;r=void 0!==n.g?n.g:this,e.exports=function(e){if(e.CSS&&e.CSS.escape)return e.CSS.escape;var t=function(e){if(0==arguments.length)throw new TypeError("`CSS.escape` requires an argument.");for(var t,n=String(e),r=n.length,o=-1,a="",i=n.charCodeAt(0);++o=1&&t<=31||127==t||0==o&&t>=48&&t<=57||1==o&&t>=48&&t<=57&&45==i?"\\"+t.toString(16)+" ":0==o&&1==r&&45==t||!(t>=128||45==t||95==t||t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122)?"\\"+n.charAt(o):n.charAt(o):a+="�";return a};return e.CSS||(e.CSS={}),e.CSS.escape=t,t}(r)},27698:(e,t,n)=>{"use strict";var r=n(48764).Buffer;function o(e){return e instanceof r||e instanceof Date||e instanceof RegExp}function a(e){if(e instanceof r){var t=r.alloc?r.alloc(e.length):new r(e.length);return e.copy(t),t}if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return new RegExp(e);throw new Error("Unexpected situation")}function i(e){var t=[];return e.forEach((function(e,n){"object"==typeof e&&null!==e?Array.isArray(e)?t[n]=i(e):o(e)?t[n]=a(e):t[n]=l({},e):t[n]=e})),t}function s(e,t){return"__proto__"===t?void 0:e[t]}var l=e.exports=function(){if(arguments.length<1||"object"!=typeof arguments[0])return!1;if(arguments.length<2)return arguments[0];var e,t,n=arguments[0];return Array.prototype.slice.call(arguments,1).forEach((function(r){"object"!=typeof r||null===r||Array.isArray(r)||Object.keys(r).forEach((function(u){return t=s(n,u),(e=s(r,u))===n?void 0:"object"!=typeof e||null===e?void(n[u]=e):Array.isArray(e)?void(n[u]=i(e)):o(e)?void(n[u]=a(e)):"object"!=typeof t||null===t||Array.isArray(t)?void(n[u]=l({},e)):void(n[u]=l(t,e))}))})),n}},9996:e=>{"use strict";var t=function(e){return function(e){return!!e&&"object"==typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||function(e){return e.$$typeof===n}(e)}(e)};var n="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function r(e,t){return!1!==t.clone&&t.isMergeableObject(e)?l((n=e,Array.isArray(n)?[]:{}),e,t):e;var n}function o(e,t,n){return e.concat(t).map((function(e){return r(e,n)}))}function a(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter((function(t){return Object.propertyIsEnumerable.call(e,t)})):[]}(e))}function i(e,t){try{return t in e}catch(e){return!1}}function s(e,t,n){var o={};return n.isMergeableObject(e)&&a(e).forEach((function(t){o[t]=r(e[t],n)})),a(t).forEach((function(a){(function(e,t){return i(e,t)&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))})(e,a)||(i(e,a)&&n.isMergeableObject(t[a])?o[a]=function(e,t){if(!t.customMerge)return l;var n=t.customMerge(e);return"function"==typeof n?n:l}(a,n)(e[a],t[a],n):o[a]=r(t[a],n))})),o}function l(e,n,a){(a=a||{}).arrayMerge=a.arrayMerge||o,a.isMergeableObject=a.isMergeableObject||t,a.cloneUnlessOtherwiseSpecified=r;var i=Array.isArray(n);return i===Array.isArray(e)?i?a.arrayMerge(e,n,a):s(e,n,a):r(n,a)}l.all=function(e,t){if(!Array.isArray(e))throw new Error("first argument should be an array");return e.reduce((function(e,n){return l(e,n,t)}),{})};var u=l;e.exports=u},27856:function(e){e.exports=function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,n){return t=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},t(e,n)}function n(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function r(e,o,a){return r=n()?Reflect.construct:function(e,n,r){var o=[null];o.push.apply(o,n);var a=new(Function.bind.apply(e,o));return r&&t(a,r.prototype),a},r.apply(null,arguments)}function o(e){return a(e)||i(e)||s(e)||u()}function a(e){if(Array.isArray(e))return l(e)}function i(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}function s(e,t){if(e){if("string"==typeof e)return l(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?l(e,t):void 0}}function l(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1?n-1:0),o=1;o/gm),G=g(/^data-[\-\w.\u00B7-\uFFFF]/),Z=g(/^aria-[\-\w]+$/),Y=g(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),Q=g(/^(?:\w+script|data):/i),X=g(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),ee=g(/^html$/i),te=function(){return"undefined"==typeof window?null:window},ne=function(t,n){if("object"!==e(t)||"function"!=typeof t.createPolicy)return null;var r=null,o="data-tt-policy-suffix";n.currentScript&&n.currentScript.hasAttribute(o)&&(r=n.currentScript.getAttribute(o));var a="dompurify"+(r?"#"+r:"");try{return t.createPolicy(a,{createHTML:function(e){return e},createScriptURL:function(e){return e}})}catch(e){return console.warn("TrustedTypes policy "+a+" could not be created."),null}};function re(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:te(),n=function(e){return re(e)};if(n.version="2.3.10",n.removed=[],!t||!t.document||9!==t.document.nodeType)return n.isSupported=!1,n;var r=t.document,a=t.document,i=t.DocumentFragment,s=t.HTMLTemplateElement,l=t.Node,u=t.Element,c=t.NodeFilter,p=t.NamedNodeMap,f=void 0===p?t.NamedNodeMap||t.MozNamedAttrMap:p,h=t.HTMLFormElement,d=t.DOMParser,g=t.trustedTypes,y=u.prototype,v=M(y,"cloneNode"),b=M(y,"nextSibling"),w=M(y,"childNodes"),I=M(y,"parentNode");if("function"==typeof s){var N=a.createElement("template");N.content&&N.content.ownerDocument&&(a=N.content.ownerDocument)}var oe=ne(g,r),ae=oe?oe.createHTML(""):"",ie=a,se=ie.implementation,le=ie.createNodeIterator,ue=ie.createDocumentFragment,ce=ie.getElementsByTagName,pe=r.importNode,fe={};try{fe=R(a).documentMode?a.documentMode:{}}catch(e){}var he={};n.isSupported="function"==typeof I&&se&&void 0!==se.createHTMLDocument&&9!==fe;var de,me,ge=J,ye=K,ve=G,be=Z,we=Q,Ee=X,xe=Y,_e=null,Se=P({},[].concat(o(D),o(L),o(B),o(U),o(q))),Ae=null,Ce=P({},[].concat(o($),o(V),o(H),o(W))),ke=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Oe=null,je=null,Te=!0,Ie=!0,Ne=!1,Pe=!1,Re=!1,Me=!1,De=!1,Le=!1,Be=!1,Fe=!1,Ue=!0,ze=!0,qe=!1,$e={},Ve=null,He=P({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),We=null,Je=P({},["audio","video","img","source","image","track"]),Ke=null,Ge=P({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Ze="http://www.w3.org/1998/Math/MathML",Ye="http://www.w3.org/2000/svg",Qe="http://www.w3.org/1999/xhtml",Xe=Qe,et=!1,tt=["application/xhtml+xml","text/html"],nt="text/html",rt=null,ot=a.createElement("form"),at=function(e){return e instanceof RegExp||e instanceof Function},it=function(t){rt&&rt===t||(t&&"object"===e(t)||(t={}),t=R(t),de=de=-1===tt.indexOf(t.PARSER_MEDIA_TYPE)?nt:t.PARSER_MEDIA_TYPE,me="application/xhtml+xml"===de?function(e){return e}:S,_e="ALLOWED_TAGS"in t?P({},t.ALLOWED_TAGS,me):Se,Ae="ALLOWED_ATTR"in t?P({},t.ALLOWED_ATTR,me):Ce,Ke="ADD_URI_SAFE_ATTR"in t?P(R(Ge),t.ADD_URI_SAFE_ATTR,me):Ge,We="ADD_DATA_URI_TAGS"in t?P(R(Je),t.ADD_DATA_URI_TAGS,me):Je,Ve="FORBID_CONTENTS"in t?P({},t.FORBID_CONTENTS,me):He,Oe="FORBID_TAGS"in t?P({},t.FORBID_TAGS,me):{},je="FORBID_ATTR"in t?P({},t.FORBID_ATTR,me):{},$e="USE_PROFILES"in t&&t.USE_PROFILES,Te=!1!==t.ALLOW_ARIA_ATTR,Ie=!1!==t.ALLOW_DATA_ATTR,Ne=t.ALLOW_UNKNOWN_PROTOCOLS||!1,Pe=t.SAFE_FOR_TEMPLATES||!1,Re=t.WHOLE_DOCUMENT||!1,Le=t.RETURN_DOM||!1,Be=t.RETURN_DOM_FRAGMENT||!1,Fe=t.RETURN_TRUSTED_TYPE||!1,De=t.FORCE_BODY||!1,Ue=!1!==t.SANITIZE_DOM,ze=!1!==t.KEEP_CONTENT,qe=t.IN_PLACE||!1,xe=t.ALLOWED_URI_REGEXP||xe,Xe=t.NAMESPACE||Qe,t.CUSTOM_ELEMENT_HANDLING&&at(t.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(ke.tagNameCheck=t.CUSTOM_ELEMENT_HANDLING.tagNameCheck),t.CUSTOM_ELEMENT_HANDLING&&at(t.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(ke.attributeNameCheck=t.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),t.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof t.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(ke.allowCustomizedBuiltInElements=t.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Pe&&(Ie=!1),Be&&(Le=!0),$e&&(_e=P({},o(q)),Ae=[],!0===$e.html&&(P(_e,D),P(Ae,$)),!0===$e.svg&&(P(_e,L),P(Ae,V),P(Ae,W)),!0===$e.svgFilters&&(P(_e,B),P(Ae,V),P(Ae,W)),!0===$e.mathMl&&(P(_e,U),P(Ae,H),P(Ae,W))),t.ADD_TAGS&&(_e===Se&&(_e=R(_e)),P(_e,t.ADD_TAGS,me)),t.ADD_ATTR&&(Ae===Ce&&(Ae=R(Ae)),P(Ae,t.ADD_ATTR,me)),t.ADD_URI_SAFE_ATTR&&P(Ke,t.ADD_URI_SAFE_ATTR,me),t.FORBID_CONTENTS&&(Ve===He&&(Ve=R(Ve)),P(Ve,t.FORBID_CONTENTS,me)),ze&&(_e["#text"]=!0),Re&&P(_e,["html","head","body"]),_e.table&&(P(_e,["tbody"]),delete Oe.tbody),m&&m(t),rt=t)},st=P({},["mi","mo","mn","ms","mtext"]),lt=P({},["foreignobject","desc","title","annotation-xml"]),ut=P({},["title","style","font","a","script"]),ct=P({},L);P(ct,B),P(ct,F);var pt=P({},U);P(pt,z);var ft=function(e){var t=I(e);t&&t.tagName||(t={namespaceURI:Qe,tagName:"template"});var n=S(e.tagName),r=S(t.tagName);return e.namespaceURI===Ye?t.namespaceURI===Qe?"svg"===n:t.namespaceURI===Ze?"svg"===n&&("annotation-xml"===r||st[r]):Boolean(ct[n]):e.namespaceURI===Ze?t.namespaceURI===Qe?"math"===n:t.namespaceURI===Ye?"math"===n&<[r]:Boolean(pt[n]):e.namespaceURI===Qe&&!(t.namespaceURI===Ye&&!lt[r])&&!(t.namespaceURI===Ze&&!st[r])&&!pt[n]&&(ut[n]||!ct[n])},ht=function(e){_(n.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){try{e.outerHTML=ae}catch(t){e.remove()}}},dt=function(e,t){try{_(n.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){_(n.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!Ae[e])if(Le||Be)try{ht(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},mt=function(e){var t,n;if(De)e=""+e;else{var r=A(e,/^[\r\n\t ]+/);n=r&&r[0]}"application/xhtml+xml"===de&&(e=''+e+"");var o=oe?oe.createHTML(e):e;if(Xe===Qe)try{t=(new d).parseFromString(o,de)}catch(e){}if(!t||!t.documentElement){t=se.createDocument(Xe,"template",null);try{t.documentElement.innerHTML=et?"":o}catch(e){}}var i=t.body||t.documentElement;return e&&n&&i.insertBefore(a.createTextNode(n),i.childNodes[0]||null),Xe===Qe?ce.call(t,Re?"html":"body")[0]:Re?t.documentElement:i},gt=function(e){return le.call(e.ownerDocument||e,e,c.SHOW_ELEMENT|c.SHOW_COMMENT|c.SHOW_TEXT,null,!1)},yt=function(e){return e instanceof h&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof f)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore)},vt=function(t){return"object"===e(l)?t instanceof l:t&&"object"===e(t)&&"number"==typeof t.nodeType&&"string"==typeof t.nodeName},bt=function(e,t,r){he[e]&&E(he[e],(function(e){e.call(n,t,r,rt)}))},wt=function(e){var t;if(bt("beforeSanitizeElements",e,null),yt(e))return ht(e),!0;if(j(/[\u0080-\uFFFF]/,e.nodeName))return ht(e),!0;var r=me(e.nodeName);if(bt("uponSanitizeElement",e,{tagName:r,allowedTags:_e}),e.hasChildNodes()&&!vt(e.firstElementChild)&&(!vt(e.content)||!vt(e.content.firstElementChild))&&j(/<[/\w]/g,e.innerHTML)&&j(/<[/\w]/g,e.textContent))return ht(e),!0;if("select"===r&&j(/