diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..4220d685c5 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at help@akto.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..e4f7434c5f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,46 @@ +# Contribution Guidelines + +Thank you for considering contributing to our open source API security product! We appreciate any and all help to make our product better for everyone. + +This documentation contains a set of guidelines to help you during the contribution process. +We are happy to welcome all the contributions from anyone willing to **improve/add** to this project. +Thank you for helping out and remember, **no contribution is too small.** + +# How to Contribute 👩‍💻👨‍💻 + +Below you will find the process and workflow used to review and merge your changes. + +1. Fork the repository on GitHub. +2. Create a new branch for your changes. +3. Make your changes, and ensure that all tests pass. +4. Submit a pull request, with a clear explanation of your changes and why they are needed. + +## Need more help? 🤔 + +You can refer to the following articles of Git and GitHub basics. In case you are stuck, feel free to contact the Project Mentors and Community by joining [Akto Community](https://akto.io) slack channel. + +- [Forking a Repo](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) +- [Cloning a Repo](https://help.github.com/en/desktop/contributing-to-projects/creating-an-issue-or-pull-request) +- [How to create a Pull Request](https://opensource.com/article/19/7/create-pull-request-github) +- [Getting started with Git and GitHub](https://towardsdatascience.com/getting-started-with-git-and-github-6fcd0f2d4ac6) +- [Learn GitHub from Scratch](https://lab.github.com/githubtraining/introduction-to-github) + + +## Code of Conduct + +We have a Code of Conduct that we expect all contributors to follow. Please read [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) before contributing. + +## Reporting Issues + +If you find a bug or have an idea for a new feature, please open an issue on GitHub. Be sure to include a clear description and any relevant information, such as the version of the product you are using. + +## License + +By contributing to this project, you agree that your contributions will be licensed under the [LICENSE](LICENSE) file. + +## Contact + +If you have any question, you can contact the maintainers of this project via the email: contact@akto.io + +Thank you for your interest in contributing to our project! We look forward to working with you. + diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..89fcd732d7 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2023 Akto.io. + +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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000000..6d532082d5 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# Akto.io API Security + +This is an open-source API security product that helps to protect your API from various attacks such as SQL injection, cross-site scripting (XSS), and cross-site request forgery (CSRF). + +## Features + +- Get your API Inventory +- Run automated tests for BOLA, BUA, JWT tests +- Import data from Burpsuite and Postman + +## Getting Started + +### Quick local deploy +Execute the following command: +`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/akto-api-security/infra/feature/self_hosting/cf-deploy-akto)"` + +### Deploy (Suggest better title here) + +#### Prerequisites +OpenJDK 8, node, npm, maven (v3.6.3), Mongo + + +#### Clone repo +1. `mkdir ~/akto_code` +2. `cd akto_code` +3. `git clone https://github.com/akto-api-security/mono` Link to be updated as per the open source repo + +#### Setup database + +1. `cd ~` +2. `mkdir ~/akto_mongo_data` +3. `/bin/mongod --dbpath ~/akto_mongo_data` + +#### Setup Frontend + +1. `cd ~/akto_code/mono` +2. `cd apps/dashboard` +3. `npm install` +4. `npm run hot` + +#### Setup Backend +1. `cd ~/akto_code/mono` +2. `export AKTO_MONGO_CONN="mongodb://localhost:27017"` +3. `mvn clean install` +4. `mvn --projects :dashboard --also-make jetty:run` + +#### Play around +1. Open `localhost:8080` in your favourite browser +2. You will need to signup when logging in for the first time, next time onwards you can login + +## Usage + +You can use this product as a middleware in your express.js or any other node.js based web application. + +## Contributing + +We welcome contributions to this project. Please read our [CONTRIBUTING.md](CONTRIBUTING.md) for more information on how to get involved. + +## License + +This project is licensed under the [MIT License](LICENSE). diff --git a/apps/api-runtime/.gitignore b/apps/api-runtime/.gitignore new file mode 100644 index 0000000000..a8954163ef --- /dev/null +++ b/apps/api-runtime/.gitignore @@ -0,0 +1,12 @@ +.idea +target/ +output/ +gen/ +yarn.lock +.DS_Store +*.iml +*.pem +*.env +.settings +.project +.classpath \ No newline at end of file diff --git a/apps/api-runtime/Dockerfile b/apps/api-runtime/Dockerfile new file mode 100644 index 0000000000..1a09568f0e --- /dev/null +++ b/apps/api-runtime/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk +WORKDIR /app +COPY ./target/api-runtime-1.0-SNAPSHOT-jar-with-dependencies.jar /app/api-runtime-1.0-SNAPSHOT-jar-with-dependencies.jar +CMD "java" "-XX:+ExitOnOutOfMemoryError" "-jar" "/app/api-runtime-1.0-SNAPSHOT-jar-with-dependencies.jar" \ No newline at end of file diff --git a/apps/api-runtime/README.md b/apps/api-runtime/README.md new file mode 100644 index 0000000000..e265b291fc --- /dev/null +++ b/apps/api-runtime/README.md @@ -0,0 +1 @@ +# api-runtime \ No newline at end of file diff --git a/apps/api-runtime/pom.xml b/apps/api-runtime/pom.xml new file mode 100644 index 0000000000..552aa1a068 --- /dev/null +++ b/apps/api-runtime/pom.xml @@ -0,0 +1,141 @@ + + + 4.0.0 + + + com.akto.apps + apps + ${revision} + + + com.akto.apps.api-runtime + api-runtime + jar + + + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.slf4j + slf4j-simple + 1.7.32 + + + com.akto.libs.dao + dao + ${project.version} + + + com.akto.libs.utils + utils + ${project.version} + + + org.apache.httpcomponents + httpclient + 4.5.13 + + + org.jetbrains + annotations + RELEASE + compile + + + org.junit.jupiter + junit-jupiter-api + 5.4.2 + test + + + com.fasterxml.jackson.core + jackson-databind + 2.12.7.1 + compile + + + org.apache.kafka + kafka-clients + 3.0.0 + + + com.akto.libs.utils + utils + test-jar + ${project.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 8 + 8 + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.0.1 + + + copy-dependencies + package + + copy-dependencies + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + + + com.akto.runtime.Main + + + + + jar-with-dependencies + + + + + + + + src/main/java + src/test/java + + + src/main/resources + true + + **/version.txt + + + + + + diff --git a/apps/api-runtime/src/main/java/com/akto/parsers/HttpCallParser.java b/apps/api-runtime/src/main/java/com/akto/parsers/HttpCallParser.java new file mode 100644 index 0000000000..b7b3e354c3 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/parsers/HttpCallParser.java @@ -0,0 +1,296 @@ +package com.akto.parsers; + +import java.net.URLDecoder; +import java.util.*; + +import com.akto.DaoInit; +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.context.Context; +import com.akto.dto.*; +import com.akto.runtime.APICatalogSync; +import com.akto.runtime.URLAggregator; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; + +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.FindOneAndUpdateOptions; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.Updates; +import org.apache.commons.lang3.math.NumberUtils; +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HttpCallParser { + + private static final String AKTO_REQUEST = "##AKTO_REQUEST##"; + private static final String AKTO_RESPONSE = "##AKTO_RESPONSE##"; + private final static ObjectMapper mapper = new ObjectMapper(); + private final int sync_threshold_count; + private final int sync_threshold_time; + private int sync_count = 0; + private int last_synced; + private static final Logger logger = LoggerFactory.getLogger(HttpCallParser.class); + + public APICatalogSync apiCatalogSync; + private Map hostNameToIdMap = new HashMap<>(); + + public HttpCallParser(String userIdentifier, int thresh, int sync_threshold_count, int sync_threshold_time, boolean fetchAllSTI) { + last_synced = 0; + this.sync_threshold_count = sync_threshold_count; + this.sync_threshold_time = sync_threshold_time; + apiCatalogSync = new APICatalogSync(userIdentifier,thresh); + apiCatalogSync.buildFromDB(false, fetchAllSTI); + } + + public static HttpResponseParams parseKafkaMessage(String message) throws Exception { + + //convert java object to JSON format + Map json = gson.fromJson(message, Map.class); + + String method = (String) json.get("method"); + String url = (String) json.get("path"); + String type = (String) json.get("type"); + Map> requestHeaders = OriginalHttpRequest.buildHeadersMap(json, "requestHeaders"); + + String rawRequestPayload = (String) json.get("requestPayload"); + String requestPayload = OriginalHttpRequest.rawToJsonString(rawRequestPayload,requestHeaders); + + String apiCollectionIdStr = json.getOrDefault("akto_vxlan_id", "0").toString(); + int apiCollectionId = 0; + if (NumberUtils.isDigits(apiCollectionIdStr)) { + apiCollectionId = NumberUtils.toInt(apiCollectionIdStr, 0); + } + + HttpRequestParams requestParams = new HttpRequestParams( + method,url,type, requestHeaders, requestPayload, apiCollectionId + ); + + int statusCode = Integer.parseInt(json.get("statusCode").toString()); + String status = (String) json.get("status"); + Map> responseHeaders = OriginalHttpRequest.buildHeadersMap(json, "responseHeaders"); + String payload = (String) json.get("responsePayload"); + int time = Integer.parseInt(json.get("time").toString()); + String accountId = (String) json.get("akto_account_id"); + String sourceIP = (String) json.get("ip"); + + String isPendingStr = (String) json.getOrDefault("is_pending", "false"); + boolean isPending = !isPendingStr.toLowerCase().equals("false"); + String sourceStr = (String) json.getOrDefault("source", HttpResponseParams.Source.OTHER.name()); + HttpResponseParams.Source source = HttpResponseParams.Source.valueOf(sourceStr); + + return new HttpResponseParams( + type,statusCode, status, responseHeaders, payload, requestParams, time, accountId, isPending, source, message, sourceIP + ); + + } + + private static final Gson gson = new Gson(); + + public static String getHeaderValue(Map> headers, String headerKey) { + if (headers == null) return null; + for (String k: headers.keySet()) { + if (k.equalsIgnoreCase(headerKey)) { + List hosts = headers.getOrDefault(k, new ArrayList<>()); + if (hosts.size() > 0) return hosts.get(0); + return null; + } + } + return null; + } + + public int createCollectionSimple(int vxlanId) { + UpdateOptions updateOptions = new UpdateOptions(); + updateOptions.upsert(true); + + ApiCollectionsDao.instance.getMCollection().updateOne( + Filters.eq("_id", vxlanId), + Updates.combine( + Updates.set(ApiCollection.VXLAN_ID, vxlanId), + Updates.setOnInsert("startTs", Context.now()), + Updates.setOnInsert("urls", new HashSet<>()) + ), + updateOptions + ); + return vxlanId; + } + + + public int createCollectionBasedOnHostName(int id, String host) throws Exception { + FindOneAndUpdateOptions updateOptions = new FindOneAndUpdateOptions(); + updateOptions.upsert(true); + // 3 cases + // 1. If 2 threads are trying to insert same host simultaneously then both will succeed with upsert true + // 2. If we are trying to insert different host but same id (hashCode collision) then it will fail, + // so we loop 20 times till we succeed + boolean flag = false; + for (int i=0;i < 100; i++) { + id += i; + try { + Bson updates = Updates.combine( + Updates.setOnInsert(ApiCollection.HOST_NAME, host), + Updates.setOnInsert("startTs", Context.now()), + Updates.setOnInsert("urls", new HashSet<>()) + ); + + ApiCollectionsDao.instance.getMCollection().findOneAndUpdate(Filters.eq("_id", id), updates, updateOptions); + + flag = true; + break; + } catch (Exception e) { + logger.error("Error while inserting apiCollection, trying again " + i + " " + e.getMessage()); + } + } + if (flag) { // flag tells if we were successfully able to insert collection + return id; + } else { + throw new Exception("Not able to insert"); + } + } + + + int numberOfSyncs = 0; + + public APICatalogSync syncFunction(List responseParams, boolean syncImmediately, boolean fetchAllSTI) { + // USE ONLY filteredResponseParams and not responseParams + List filteredResponseParams = filterHttpResponseParams(responseParams); + boolean isHarOrPcap = aggregate(filteredResponseParams); + + for (int apiCollectionId: aggregatorMap.keySet()) { + URLAggregator aggregator = aggregatorMap.get(apiCollectionId); + apiCatalogSync.computeDelta(aggregator, false, apiCollectionId); + } + + this.sync_count += filteredResponseParams.size(); + int syncThresh = numberOfSyncs < 10 ? 10000 : sync_threshold_count; + if (syncImmediately || this.sync_count >= syncThresh || (Context.now() - this.last_synced) > this.sync_threshold_time || isHarOrPcap) { + numberOfSyncs++; + apiCatalogSync.syncWithDB(syncImmediately, fetchAllSTI); + this.last_synced = Context.now(); + this.sync_count = 0; + return apiCatalogSync; + } + + return null; + } + + public static boolean useHostCondition(String hostName, HttpResponseParams.Source source) { + List whiteListSource = Arrays.asList(HttpResponseParams.Source.MIRRORING); + boolean hostNameCondition; + if (hostName == null) { + hostNameCondition = false; + } else { + hostNameCondition = ! ( hostName.toLowerCase().equals(hostName.toUpperCase()) ); + } + return whiteListSource.contains(source) && hostNameCondition && ApiCollection.useHost; + } + + + public List filterHttpResponseParams(List httpResponseParamsList) { + List filteredResponseParams = new ArrayList<>(); + for (HttpResponseParams httpResponseParam: httpResponseParamsList) { + boolean cond = HttpResponseParams.validHttpResponseCode(httpResponseParam.getStatusCode()); + if (!cond) continue; + + String ignoreAktoFlag = getHeaderValue(httpResponseParam.getRequestParams().getHeaders(), AccountSettings.AKTO_IGNORE_FLAG); + if (ignoreAktoFlag != null) continue; + + String hostName = getHeaderValue(httpResponseParam.getRequestParams().getHeaders(), "host"); + + int vxlanId = httpResponseParam.requestParams.getApiCollectionId(); + int apiCollectionId ; + + if (useHostCondition(hostName, httpResponseParam.getSource())) { + hostName = hostName.toLowerCase(); + hostName = hostName.trim(); + + String key = hostName; + + if (hostNameToIdMap.containsKey(key)) { + apiCollectionId = hostNameToIdMap.get(key); + + } else { + int id = hostName.hashCode(); + try { + + apiCollectionId = createCollectionBasedOnHostName(id, hostName); + + hostNameToIdMap.put(key, apiCollectionId); + } catch (Exception e) { + logger.error("Failed to create collection for host : " + hostName); + createCollectionSimple(vxlanId); + hostNameToIdMap.put("null " + vxlanId, vxlanId); + apiCollectionId = httpResponseParam.requestParams.getApiCollectionId(); + } + } + + } else { + String key = "null" + " " + vxlanId; + if (!hostNameToIdMap.containsKey(key)) { + createCollectionSimple(vxlanId); + hostNameToIdMap.put(key, vxlanId); + } + + apiCollectionId = vxlanId; + } + + httpResponseParam.requestParams.setApiCollectionId(apiCollectionId); + filteredResponseParams.add(httpResponseParam); + } + + return filteredResponseParams; + } + + private Map aggregatorMap = new HashMap<>(); + + public void setAggregatorMap(Map aggregatorMap){ + this.aggregatorMap=aggregatorMap; + } + + public Map getAggregatorMap(){ + return this.aggregatorMap; + } + + public boolean aggregate(List responses) { + int count = 0; + boolean ret = false; + for (HttpResponseParams responseParams: responses) { + if (responseParams.getSource() == HttpResponseParams.Source.HAR || responseParams.getSource() == HttpResponseParams.Source.PCAP) { + ret = true; + } + try { + int collId = responseParams.getRequestParams().getApiCollectionId(); + URLAggregator aggregator = aggregatorMap.get(collId); + if (aggregator == null) { + aggregator = new URLAggregator(); + aggregatorMap.put(collId, aggregator); + } + + aggregator.addURL(responseParams); + count++; + } catch (Exception e) { + + } + } + + logger.info("added " + count + " urls"); + return ret; + } + + public int getLastSyncTime() { + return this.last_synced; + } + + public int getSyncCount() { + return this.sync_count; + } + + public Map getHostNameToIdMap() { + return hostNameToIdMap; + } + + public void setHostNameToIdMap(Map hostNameToIdMap) { + this.hostNameToIdMap = hostNameToIdMap; + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/APICatalogSync.java b/apps/api-runtime/src/main/java/com/akto/runtime/APICatalogSync.java new file mode 100644 index 0000000000..0842342340 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/APICatalogSync.java @@ -0,0 +1,1382 @@ +package com.akto.runtime; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +import com.akto.dao.*; +import com.akto.dao.context.Context; +import com.akto.dto.*; +import com.akto.dto.HttpResponseParams.Source; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.traffic.TrafficInfo; +import com.akto.dto.traffic.Key; +import com.akto.dto.type.APICatalog; +import com.akto.dto.type.KeyTypes; +import com.akto.dto.type.RequestTemplate; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.TrafficRecorder; +import com.akto.dto.type.URLStatic; +import com.akto.dto.type.URLTemplate; +import com.akto.dto.type.SingleTypeInfo.SubType; +import com.akto.dto.type.SingleTypeInfo.SuperType; +import com.akto.dto.type.URLMethods.Method; +import com.akto.parsers.HttpCallParser; +import com.akto.runtime.merge.MergeOnHostOnly; +import com.akto.task.Cluster; +import com.akto.types.CappedSet; +import com.akto.utils.RedactSampleData; +import com.mongodb.BasicDBObject; +import com.mongodb.bulk.BulkWriteResult; +import com.mongodb.client.model.*; + +import org.apache.commons.lang3.math.NumberUtils; +import org.bson.conversions.Bson; +import org.bson.json.JsonParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.akto.dto.type.KeyTypes.patternToSubType; + +public class APICatalogSync { + + public int thresh; + public String userIdentifier; + private static final Logger logger = LoggerFactory.getLogger(APICatalogSync.class); + public Map dbState; + public Map delta; + public Map sensitiveParamInfoBooleanMap; + public static boolean mergeAsyncOutside = false; + + public APICatalogSync(String userIdentifier,int thresh) { + this.thresh = thresh; + this.userIdentifier = userIdentifier; + this.dbState = new HashMap<>(); + this.delta = new HashMap<>(); + this.sensitiveParamInfoBooleanMap = new HashMap<>(); + try { + String instanceType = System.getenv("AKTO_INSTANCE_TYPE"); + if (instanceType != null && "RUNTIME".equalsIgnoreCase(instanceType)) { + mergeAsyncOutside = AccountSettingsDao.instance.findOne(AccountSettingsDao.generateFilter()).getMergeAsyncOutside(); + } + } catch (Exception e) { + + } + + } + + public static final int STRING_MERGING_THRESHOLD = 20; + + public void processResponse(RequestTemplate requestTemplate, Collection responses, List deletedInfo) { + Iterator iter = responses.iterator(); + while(iter.hasNext()) { + try { + processResponse(requestTemplate, iter.next(), deletedInfo); + } catch (Exception e) { + e.printStackTrace(); + logger.error("processResponse: " + e.getMessage()); + } + } + } + + public void processResponse(RequestTemplate requestTemplate, HttpResponseParams responseParams, List deletedInfo) { + HttpRequestParams requestParams = responseParams.getRequestParams(); + String urlWithParams = requestParams.getURL(); + String methodStr = requestParams.getMethod(); + URLStatic baseURL = URLAggregator.getBaseURL(urlWithParams, methodStr); + responseParams.requestParams.url = baseURL.getUrl(); + int statusCode = responseParams.getStatusCode(); + Method method = Method.fromString(methodStr); + String userId = extractUserId(responseParams, userIdentifier); + + if (!responseParams.getIsPending()) { + requestTemplate.processTraffic(responseParams.getTime()); + } + if (HttpResponseParams.validHttpResponseCode(statusCode)) { + String reqPayload = requestParams.getPayload(); + + if (reqPayload == null || reqPayload.isEmpty()) { + reqPayload = "{}"; + } + + requestTemplate.processHeaders(requestParams.getHeaders(), baseURL.getUrl(), methodStr, -1, userId, requestParams.getApiCollectionId(), responseParams.getOrig(), sensitiveParamInfoBooleanMap); + BasicDBObject payload = RequestTemplate.parseRequestPayload(requestParams, urlWithParams); + if (payload != null) { + deletedInfo.addAll(requestTemplate.process2(payload, baseURL.getUrl(), methodStr, -1, userId, requestParams.getApiCollectionId(), responseParams.getOrig(), sensitiveParamInfoBooleanMap)); + } + requestTemplate.recordMessage(responseParams.getOrig()); + } + + Map responseTemplates = requestTemplate.getResponseTemplates(); + + RequestTemplate responseTemplate = responseTemplates.get(statusCode); + if (responseTemplate == null) { + responseTemplate = new RequestTemplate(new HashMap<>(), null, new HashMap<>(), new TrafficRecorder(new HashMap<>())); + responseTemplates.put(statusCode, responseTemplate); + } + + try { + String respPayload = responseParams.getPayload(); + + if (respPayload == null || respPayload.isEmpty()) { + respPayload = "{}"; + } + + if(respPayload.startsWith("[")) { + respPayload = "{\"json\": "+respPayload+"}"; + } + + + BasicDBObject payload; + try { + payload = BasicDBObject.parse(respPayload); + } catch (Exception e) { + payload = BasicDBObject.parse("{}"); + } + + deletedInfo.addAll(responseTemplate.process2(payload, baseURL.getUrl(), methodStr, statusCode, userId, requestParams.getApiCollectionId(), responseParams.getOrig(), sensitiveParamInfoBooleanMap)); + responseTemplate.processHeaders(responseParams.getHeaders(), baseURL.getUrl(), method.name(), statusCode, userId, requestParams.getApiCollectionId(), responseParams.getOrig(), sensitiveParamInfoBooleanMap); + if (!responseParams.getIsPending()) { + responseTemplate.processTraffic(responseParams.getTime()); + } + + } catch (JsonParseException e) { + logger.error("Failed to parse json payload " + e.getMessage()); + } + } + + public static String extractUserId(HttpResponseParams responseParams, String userIdentifier) { + List token = responseParams.getRequestParams().getHeaders().get(userIdentifier); + if (token == null || token.size() == 0) { + return "HC"; + } else { + return token.get(0); + } + } + + int countUsers(Set responseParamsList) { + Set users = new HashSet<>(); + for(HttpResponseParams responseParams: responseParamsList) { + users.add(extractUserId(responseParams, userIdentifier)); + } + + return users.size(); + } + + public void computeDelta(URLAggregator origAggregator, boolean triggerTemplateGeneration, int apiCollectionId) { + long start = System.currentTimeMillis(); + + APICatalog deltaCatalog = this.delta.get(apiCollectionId); + if (deltaCatalog == null) { + deltaCatalog = new APICatalog(apiCollectionId, new HashMap<>(), new HashMap<>()); + this.delta.put(apiCollectionId, deltaCatalog); + } + + APICatalog dbCatalog = this.dbState.get(apiCollectionId); + if (dbCatalog == null) { + dbCatalog = new APICatalog(apiCollectionId, new HashMap<>(), new HashMap<>()); + this.dbState.put(apiCollectionId, dbCatalog); + } + + URLAggregator aggregator = new URLAggregator(origAggregator.urls); + origAggregator.urls = new ConcurrentHashMap<>(); + + start = System.currentTimeMillis(); + processKnownStaticURLs(aggregator, deltaCatalog, dbCatalog); + + start = System.currentTimeMillis(); + Map pendingRequests = createRequestTemplates(aggregator); + + start = System.currentTimeMillis(); + tryWithKnownURLTemplates(pendingRequests, deltaCatalog, dbCatalog, apiCollectionId ); + + if (!mergeAsyncOutside) { + start = System.currentTimeMillis(); + tryMergingWithKnownStrictURLs(pendingRequests, dbCatalog, deltaCatalog); + } else { + for (URLStatic pending: pendingRequests.keySet()) { + RequestTemplate pendingTemplate = pendingRequests.get(pending); + RequestTemplate rt = deltaCatalog.getStrictURLToMethods().get(pending); + if (rt != null) { + rt.mergeFrom(pendingTemplate); + } else { + deltaCatalog.getStrictURLToMethods().put(pending, pendingTemplate); + } + } + } + } + + + public static ApiMergerResult tryMergeURLsInCollection(int apiCollectionId) { + ApiCollection apiCollection = ApiCollectionsDao.instance.getMeta(apiCollectionId); + + Bson filterQ = null; + if (apiCollection != null && apiCollection.getHostName() == null) { + filterQ = Filters.eq("apiCollectionId", apiCollectionId); + } else { + filterQ = Filters.and( + Filters.eq("apiCollectionId", apiCollectionId), + Filters.or(Filters.eq("isHeader", false), Filters.eq("param", "host")) + ); + } + + int offset = 0; + int limit = 1_000_000; + + List singleTypeInfos = new ArrayList<>(); + ApiMergerResult finalResult = new ApiMergerResult(new HashMap<>()); + do { + singleTypeInfos = SingleTypeInfoDao.instance.findAll(filterQ, offset, limit, null, Projections.exclude("values")); + + + Map> staticUrlToSti = new HashMap<>(); + List templateUrls = new ArrayList<>(); + for(SingleTypeInfo sti: singleTypeInfos) { + String key = sti.getMethod() + " " + sti.getUrl(); + if (key.contains("INTEGER") || key.contains("STRING") || key.contains("UUID")) { + templateUrls.add(key); + continue; + }; + + if (sti.getIsUrlParam()) continue; + if (sti.getIsHeader()) { + staticUrlToSti.putIfAbsent(key, new HashSet<>()); + continue; + } + + + Set set = staticUrlToSti.get(key); + if (set == null) { + set = new HashSet<>(); + staticUrlToSti.put(key, set); + } + + set.add(sti.getResponseCode() + " " + sti.getParam()); + } + + for(String staticURL: staticUrlToSti.keySet()) { + Method staticMethod = Method.fromString(staticURL.split(" ")[0]); + String staticEndpoint = staticURL.split(" ")[1]; + + for (String templateURL: templateUrls) { + Method templateMethod = Method.fromString(templateURL.split(" ")[0]); + String templateEndpoint = templateURL.split(" ")[1]; + + URLTemplate urlTemplate = createUrlTemplate(templateEndpoint, templateMethod); + if (urlTemplate.match(staticEndpoint, staticMethod)) { + finalResult.deleteStaticUrls.add(staticURL); + break; + } + } + } + + Map>> sizeToUrlToSti = groupByTokenSize(staticUrlToSti); + + sizeToUrlToSti.remove(1); + sizeToUrlToSti.remove(0); + + + for(int size: sizeToUrlToSti.keySet()) { + ApiMergerResult result = tryMergingWithKnownStrictURLs(sizeToUrlToSti.get(size)); + finalResult.templateToStaticURLs.putAll(result.templateToStaticURLs); + } + + offset += limit; + } while (!singleTypeInfos.isEmpty()); + + return finalResult; + } + + private static Map>> groupByTokenSize(Map> catalog) { + Map>> sizeToURL = new HashMap<>(); + for(String rawURLPlusMethod: catalog.keySet()) { + String[] rawUrlPlusMethodSplit = rawURLPlusMethod.split(" "); + String rawURL = rawUrlPlusMethodSplit.length > 1 ? rawUrlPlusMethodSplit[1] : rawUrlPlusMethodSplit[0]; + Set reqTemplate = catalog.get(rawURLPlusMethod); + String url = APICatalogSync.trim(rawURL); + String[] tokens = url.split("/"); + Map> urlSet = sizeToURL.get(tokens.length); + urlSet = sizeToURL.get(tokens.length); + if (urlSet == null) { + urlSet = new HashMap<>(); + sizeToURL.put(tokens.length, urlSet); + } + + urlSet.put(rawURLPlusMethod, reqTemplate); + } + + return sizeToURL; + } + + static class ApiMergerResult { + public Set deleteStaticUrls = new HashSet<>(); + Map> templateToStaticURLs = new HashMap<>(); + + public ApiMergerResult(Map> templateToSti) { + this.templateToStaticURLs = templateToSti; + } + + public String toString() { + String ret = ("templateToSti======================================================: \n"); + for(URLTemplate urlTemplate: templateToStaticURLs.keySet()) { + ret += (urlTemplate.getTemplateString()) + "\n"; + for(String str: templateToStaticURLs.get(urlTemplate)) { + ret += ("\t " + str + "\n"); + } + } + + return ret; + } + } + + private static ApiMergerResult tryMergingWithKnownStrictURLs( + Map> pendingRequests + ) { + Map> templateToStaticURLs = new HashMap<>(); + + Iterator>> iterator = pendingRequests.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + iterator.remove(); + + String newUrl = entry.getKey(); + Set newTemplate = entry.getValue(); + Method newMethod = Method.fromString(newUrl.split(" ")[0]); + String newEndpoint = newUrl.split(" ")[1]; + + boolean matchedInDeltaTemplate = false; + for(URLTemplate urlTemplate: templateToStaticURLs.keySet()){ + if (urlTemplate.match(newEndpoint, newMethod)) { + matchedInDeltaTemplate = true; + templateToStaticURLs.get(urlTemplate).add(newUrl); + break; + } + } + + if (matchedInDeltaTemplate) { + continue; + } + + int countSimilarURLs = 0; + Map>> potentialMerges = new HashMap<>(); + for(String aUrl: pendingRequests.keySet()) { + Set aTemplate = pendingRequests.get(aUrl); + Method aMethod = Method.fromString(aUrl.split(" ")[0]); + String aEndpoint = aUrl.split(" ")[1]; + URLStatic aStatic = new URLStatic(aEndpoint, aMethod); + URLStatic newStatic = new URLStatic(newEndpoint, newMethod); + URLTemplate mergedTemplate = APICatalogSync.tryMergeUrls(aStatic, newStatic); + if (mergedTemplate == null) { + continue; + } + + if (APICatalogSync.areBothUuidUrls(newStatic,aStatic,mergedTemplate) || RequestTemplate.compareKeys(aTemplate, newTemplate, mergedTemplate)) { + Map> similarTemplates = potentialMerges.get(mergedTemplate); + if (similarTemplates == null) { + similarTemplates = new HashMap<>(); + potentialMerges.put(mergedTemplate, similarTemplates); + } + similarTemplates.put(aUrl, aTemplate); + + if (!RequestTemplate.isMergedOnStr(mergedTemplate) || APICatalogSync.areBothUuidUrls(newStatic,aStatic,mergedTemplate)) { + countSimilarURLs = APICatalogSync.STRING_MERGING_THRESHOLD; + } + + countSimilarURLs++; + } + } + + if (countSimilarURLs >= APICatalogSync.STRING_MERGING_THRESHOLD) { + URLTemplate mergedTemplate = potentialMerges.keySet().iterator().next(); + Set matchedStaticURLs = templateToStaticURLs.get(mergedTemplate); + + if (matchedStaticURLs == null) { + matchedStaticURLs = new HashSet<>(); + templateToStaticURLs.put(mergedTemplate, matchedStaticURLs); + } + + matchedStaticURLs.add(newUrl); + + for (Map.Entry> rt: potentialMerges.getOrDefault(mergedTemplate, new HashMap<>()).entrySet()) { + matchedStaticURLs.add(rt.getKey()); + } + } + } + + return new ApiMergerResult(templateToStaticURLs); + } + + private void tryMergingWithKnownStrictURLs( + Map pendingRequests, + APICatalog dbCatalog, + APICatalog deltaCatalog + ) { + Iterator> iterator = pendingRequests.entrySet().iterator(); + Map> dbSizeToUrlToTemplate = groupByTokenSize(dbCatalog); + Map deltaTemplates = deltaCatalog.getStrictURLToMethods(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + URLStatic newUrl = entry.getKey(); + RequestTemplate newTemplate = entry.getValue(); + String[] tokens = tokenize(newUrl.getUrl()); + + if (tokens.length == 1) { + RequestTemplate rt = deltaTemplates.get(newUrl); + if (rt != null) { + rt.mergeFrom(newTemplate); + } else { + deltaTemplates.put(newUrl, newTemplate); + } + iterator.remove(); + continue; + } + + boolean matchedInDeltaTemplate = false; + for(URLTemplate urlTemplate: deltaCatalog.getTemplateURLToMethods().keySet()){ + RequestTemplate deltaTemplate = deltaCatalog.getTemplateURLToMethods().get(urlTemplate); + if (urlTemplate.match(newUrl)) { + matchedInDeltaTemplate = true; + deltaTemplate.mergeFrom(newTemplate); + break; + } + } + + if (matchedInDeltaTemplate) { + iterator.remove(); + continue; + } + + Map dbTemplates = dbSizeToUrlToTemplate.get(tokens.length); + if (dbTemplates != null && dbTemplates.size() > 0) { + boolean newUrlMatchedInDb = false; + int countSimilarURLs = 0; + Map> potentialMerges = new HashMap<>(); + for(URLStatic dbUrl: dbTemplates.keySet()) { + RequestTemplate dbTemplate = dbTemplates.get(dbUrl); + URLTemplate mergedTemplate = tryMergeUrls(dbUrl, newUrl); + if (mergedTemplate == null) { + continue; + } + + if (areBothUuidUrls(newUrl,dbUrl,mergedTemplate) || dbTemplate.compare(newTemplate, mergedTemplate)) { + Set similarTemplates = potentialMerges.get(mergedTemplate); + if (similarTemplates == null) { + similarTemplates = new HashSet<>(); + potentialMerges.put(mergedTemplate, similarTemplates); + } + similarTemplates.add(dbTemplate); + countSimilarURLs++; + } + } + + if (countSimilarURLs >= STRING_MERGING_THRESHOLD) { + URLTemplate mergedTemplate = potentialMerges.keySet().iterator().next(); + RequestTemplate alreadyInDelta = deltaCatalog.getTemplateURLToMethods().get(mergedTemplate); + RequestTemplate dbTemplate = potentialMerges.get(mergedTemplate).iterator().next(); + + if (alreadyInDelta != null) { + alreadyInDelta.mergeFrom(newTemplate); + } else { + RequestTemplate dbCopy = dbTemplate.copy(); + dbCopy.mergeFrom(newTemplate); + deltaCatalog.getTemplateURLToMethods().put(mergedTemplate, dbCopy); + } + + alreadyInDelta = deltaCatalog.getTemplateURLToMethods().get(mergedTemplate); + + for (RequestTemplate rt: potentialMerges.getOrDefault(mergedTemplate, new HashSet<>())) { + alreadyInDelta.mergeFrom(rt); + deltaCatalog.getDeletedInfo().addAll(rt.getAllTypeInfo()); + } + deltaCatalog.getDeletedInfo().addAll(dbTemplate.getAllTypeInfo()); + + newUrlMatchedInDb = true; + } + + if (newUrlMatchedInDb) { + iterator.remove(); + continue; + } + } + + boolean newUrlMatchedInDelta = false; + + for (URLStatic deltaUrl: deltaCatalog.getStrictURLToMethods().keySet()) { + RequestTemplate deltaTemplate = deltaTemplates.get(deltaUrl); + URLTemplate mergedTemplate = tryMergeUrls(deltaUrl, newUrl); + if (mergedTemplate == null || (RequestTemplate.isMergedOnStr(mergedTemplate) && !areBothUuidUrls(newUrl,deltaUrl,mergedTemplate))) { + continue; + } + + newUrlMatchedInDelta = true; + deltaCatalog.getDeletedInfo().addAll(deltaTemplate.getAllTypeInfo()); + RequestTemplate alreadyInDelta = deltaCatalog.getTemplateURLToMethods().get(mergedTemplate); + + if (alreadyInDelta != null) { + alreadyInDelta.mergeFrom(newTemplate); + } else { + RequestTemplate deltaCopy = deltaTemplate.copy(); + deltaCopy.mergeFrom(newTemplate); + deltaCatalog.getTemplateURLToMethods().put(mergedTemplate, deltaCopy); + } + + deltaCatalog.getStrictURLToMethods().remove(deltaUrl); + break; + } + + if (newUrlMatchedInDelta) { + iterator.remove(); + continue; + } + + RequestTemplate rt = deltaTemplates.get(newUrl); + if (rt != null) { + rt.mergeFrom(newTemplate); + } else { + deltaTemplates.put(newUrl, newTemplate); + } + iterator.remove(); + } + } + + public static boolean areBothUuidUrls(URLStatic newUrl, URLStatic deltaUrl, URLTemplate mergedTemplate) { + Pattern pattern = patternToSubType.get(SingleTypeInfo.UUID); + + String[] n = tokenize(newUrl.getUrl()); + String[] o = tokenize(deltaUrl.getUrl()); + SuperType[] b = mergedTemplate.getTypes(); + for (int idx =0 ; idx < b.length; idx++) { + SuperType c = b[idx]; + if (Objects.equals(c, SuperType.STRING) && o.length > idx) { + String val = n[idx]; + if(!pattern.matcher(val).matches() || !pattern.matcher(o[idx]).matches()) { + return false; + } + } + } + + return true; + } + + + public static URLTemplate tryMergeUrls(URLStatic dbUrl, URLStatic newUrl) { + if (dbUrl.getMethod() != newUrl.getMethod()) { + return null; + } + String[] dbTokens = tokenize(dbUrl.getUrl()); + String[] newTokens = tokenize(newUrl.getUrl()); + + if (dbTokens.length != newTokens.length) { + return null; + } + + Pattern pattern = patternToSubType.get(SingleTypeInfo.UUID); + + SuperType[] newTypes = new SuperType[newTokens.length]; + int templatizedStrTokens = 0; + for(int i = 0; i < newTokens.length; i ++) { + String tempToken = newTokens[i]; + String dbToken = dbTokens[i]; + + if (tempToken.equalsIgnoreCase(dbToken)) { + continue; + } + + if (NumberUtils.isParsable(tempToken) && NumberUtils.isParsable(dbToken)) { + newTypes[i] = SuperType.INTEGER; + newTokens[i] = null; + } else if(pattern.matcher(tempToken).matches() && pattern.matcher(dbToken).matches()){ + newTypes[i] = SuperType.STRING; + newTokens[i] = null; + } else { + newTypes[i] = SuperType.STRING; + newTokens[i] = null; + templatizedStrTokens++; + } + } + + if (templatizedStrTokens <= 1) { + return new URLTemplate(newTokens, newTypes, newUrl.getMethod()); + } + + return null; + + } + + + public static void mergeUrlsAndSave(int apiCollectionId) { + ApiMergerResult result = tryMergeURLsInCollection(apiCollectionId); + ArrayList> bulkUpdatesForSti = new ArrayList<>(); + ArrayList> bulkUpdatesForSampleData = new ArrayList<>(); + ArrayList> bulkUpdatesForApiInfo = new ArrayList<>(); + + for (URLTemplate urlTemplate: result.templateToStaticURLs.keySet()) { + Set matchStaticURLs = result.templateToStaticURLs.get(urlTemplate); + + boolean isFirst = true; + for (String matchedURL: matchStaticURLs) { + Method delMethod = Method.fromString(matchedURL.split(" ")[0]); + String delEndpoint = matchedURL.split(" ")[1]; + Bson filterQ = Filters.and( + Filters.eq("apiCollectionId", apiCollectionId), + Filters.eq("method", delMethod.name()), + Filters.eq("url", delEndpoint) + ); + + Bson filterQSampleData = Filters.and( + Filters.eq("_id.apiCollectionId", apiCollectionId), + Filters.eq("_id.method", delMethod.name()), + Filters.eq("_id.url", delEndpoint) + ); + + if (isFirst) { + + String newTemplateUrl = urlTemplate.getTemplateString(); + for (int i = 0; i < urlTemplate.getTypes().length; i++) { + SuperType superType = urlTemplate.getTypes()[i]; + if (superType == null) continue; + + SingleTypeInfo.ParamId stiId = new SingleTypeInfo.ParamId(newTemplateUrl, delMethod.name(), -1, false, i+"", SingleTypeInfo.GENERIC, apiCollectionId, true); + SubType subType = KeyTypes.findSubType(i, i+"",stiId); + stiId.setSubType(subType); + SingleTypeInfo sti = new SingleTypeInfo( + stiId, new HashSet<>(), new HashSet<>(), 0, Context.now(), 0, CappedSet.create(i+""), + SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MIN_VALUE, SingleTypeInfo.ACCEPTED_MAX_VALUE); + + + // SingleTypeInfoDao.instance.insertOne(sti); + bulkUpdatesForSti.add(new InsertOneModel<>(sti)); + } + + // SingleTypeInfoDao.instance.getMCollection().updateMany(filterQ, Updates.set("url", newTemplateUrl)); + + bulkUpdatesForSti.add(new UpdateManyModel<>(filterQ, Updates.set("url", newTemplateUrl), new UpdateOptions())); + + + SampleData sd = SampleDataDao.instance.findOne(filterQSampleData); + if (sd != null) { + sd.getId().url = newTemplateUrl; + // SampleDataDao.instance.insertOne(sd); + bulkUpdatesForSampleData.add(new InsertOneModel<>(sd)); + } + + + ApiInfo apiInfo = ApiInfoDao.instance.findOne(filterQSampleData); + if (apiInfo != null) { + apiInfo.getId().url = newTemplateUrl; + // ApiInfoDao.instance.insertOne(apiInfo); + bulkUpdatesForApiInfo.add(new InsertOneModel<>(apiInfo)); + } + + isFirst = false; + } else { + bulkUpdatesForSti.add(new DeleteManyModel<>(filterQ)); + // SingleTypeInfoDao.instance.deleteAll(filterQ); + + } + + bulkUpdatesForSampleData.add(new DeleteManyModel<>(filterQSampleData)); + bulkUpdatesForApiInfo.add(new DeleteManyModel<>(filterQSampleData)); + // SampleDataDao.instance.deleteAll(filterQSampleData); + // ApiInfoDao.instance.deleteAll(filterQSampleData); + } + } + + for (String deleteStaticUrl: result.deleteStaticUrls) { + Method delMethod = Method.fromString(deleteStaticUrl.split(" ")[0]); + String delEndpoint = deleteStaticUrl.split(" ")[1]; + Bson filterQ = Filters.and( + Filters.eq("apiCollectionId", apiCollectionId), + Filters.eq("method", delMethod.name()), + Filters.eq("url", delEndpoint) + ); + + Bson filterQSampleData = Filters.and( + Filters.eq("_id.apiCollectionId", apiCollectionId), + Filters.eq("_id.method", delMethod.name()), + Filters.eq("_id.url", delEndpoint) + ); + + bulkUpdatesForSti.add(new DeleteManyModel<>(filterQ)); + bulkUpdatesForSampleData.add(new DeleteManyModel<>(filterQSampleData)); + // SingleTypeInfoDao.instance.deleteAll(filterQ); + // SampleDataDao.instance.deleteAll(filterQSampleData); + } + + if (bulkUpdatesForSti.size() > 0) { + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkUpdatesForSti, new BulkWriteOptions().ordered(false)); + } + + if (bulkUpdatesForSampleData.size() > 0) { + SampleDataDao.instance.getMCollection().bulkWrite(bulkUpdatesForSampleData, new BulkWriteOptions().ordered(false)); + } + + if (bulkUpdatesForApiInfo.size() > 0) { + ApiInfoDao.instance.getMCollection().bulkWrite(bulkUpdatesForApiInfo, new BulkWriteOptions().ordered(false)); + } + } + + private void tryWithKnownURLTemplates( + Map pendingRequests, + APICatalog deltaCatalog, + APICatalog dbCatalog, + int apiCollectionId + ) { + Iterator> iterator = pendingRequests.entrySet().iterator(); + try { + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + URLStatic newUrl = entry.getKey(); + RequestTemplate newRequestTemplate = entry.getValue(); + + for (URLTemplate urlTemplate: dbCatalog.getTemplateURLToMethods().keySet()) { + if (urlTemplate.match(newUrl)) { + RequestTemplate alreadyInDelta = deltaCatalog.getTemplateURLToMethods().get(urlTemplate); + + if (alreadyInDelta != null) { + alreadyInDelta.fillUrlParams(tokenize(newUrl.getUrl()), urlTemplate, apiCollectionId); + alreadyInDelta.mergeFrom(newRequestTemplate); + } else { + RequestTemplate dbTemplate = dbCatalog.getTemplateURLToMethods().get(urlTemplate); + RequestTemplate dbCopy = dbTemplate.copy(); + dbCopy.mergeFrom(newRequestTemplate); + dbCopy.fillUrlParams(tokenize(newUrl.getUrl()), urlTemplate, apiCollectionId); + deltaCatalog.getTemplateURLToMethods().put(urlTemplate, dbCopy); + } + iterator.remove(); + break; + } + } + } + } catch (Exception e) { + + } + } + + + private Map createRequestTemplates(URLAggregator aggregator) { + Map ret = new HashMap<>(); + List deletedInfo = new ArrayList<>(); + Iterator>> iterator = aggregator.urls.entrySet().iterator(); + try { + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + URLStatic url = entry.getKey(); + Set responseParamsList = entry.getValue(); + RequestTemplate requestTemplate = ret.get(url); + if (requestTemplate == null) { + requestTemplate = new RequestTemplate(new HashMap<>(), new HashMap<>(), new HashMap<>(), new TrafficRecorder(new HashMap<>())); + ret.put(url, requestTemplate); + } + processResponse(requestTemplate, responseParamsList, deletedInfo); + iterator.remove(); + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + + return ret; + } + + private void processKnownStaticURLs(URLAggregator aggregator, APICatalog deltaCatalog, APICatalog dbCatalog) { + Iterator>> iterator = aggregator.urls.entrySet().iterator(); + List deletedInfo = deltaCatalog.getDeletedInfo(); + try { + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + URLStatic url = entry.getKey(); + Set responseParamsList = entry.getValue(); + + RequestTemplate strictMatch = dbCatalog.getStrictURLToMethods().get(url); + if (strictMatch != null) { + Map deltaCatalogStrictURLToMethods = deltaCatalog.getStrictURLToMethods(); + RequestTemplate requestTemplate = deltaCatalogStrictURLToMethods.get(url); + if (requestTemplate == null) { + requestTemplate = strictMatch.copy(); // to further process the requestTemplate + deltaCatalogStrictURLToMethods.put(url, requestTemplate) ; + strictMatch.mergeFrom(requestTemplate); // to update the existing requestTemplate in db with new data + } + + processResponse(requestTemplate, responseParamsList, deletedInfo); + iterator.remove(); + } + + } + } catch (Exception e) { + logger.info(e.getMessage(), e); + } + } + + public static String trim(String url) { + // if (mergeAsyncOutside) { + // if ( !(url.startsWith("/") ) && !( url.startsWith("http") || url.startsWith("ftp")) ){ + // url = "/" + url; + // } + // } else { + if (url.startsWith("/")) url = url.substring(1, url.length()); + // } + + if (url.endsWith("/")) url = url.substring(0, url.length()-1); + return url; + } + + private Map> groupByTokenSize(APICatalog catalog) { + Map> sizeToURL = new HashMap<>(); + for(URLStatic rawURL: catalog.getStrictURLToMethods().keySet()) { + RequestTemplate reqTemplate = catalog.getStrictURLToMethods().get(rawURL); + if (reqTemplate.getUserIds().size() < 5) { + String url = trim(rawURL.getUrl()); + String[] tokens = url.split("/"); + Map urlSet = sizeToURL.get(tokens.length); + urlSet = sizeToURL.get(tokens.length); + if (urlSet == null) { + urlSet = new HashMap<>(); + sizeToURL.put(tokens.length, urlSet); + } + + urlSet.put(rawURL, reqTemplate); + } + } + + return sizeToURL; + } + + public static String[] tokenize(String url) { + return trim(url).split("/"); + } + + Map convertToMap(List l) { + Map ret = new HashMap<>(); + for(SingleTypeInfo e: l) { + ret.put(e.composeKey(), e); + } + + return ret; + } + + public ArrayList> getDBUpdatesForSampleData(int apiCollectionId, APICatalog currentDelta, APICatalog dbCatalog ,boolean redactSampleData, boolean forceUpdate ) { + List sampleData = new ArrayList<>(); + Map deltaStrictURLToMethods = currentDelta.getStrictURLToMethods(); + Map dbStrictURLToMethods = dbCatalog.getStrictURLToMethods(); + + for(Map.Entry entry: deltaStrictURLToMethods.entrySet()) { + if (forceUpdate || !dbStrictURLToMethods.containsKey(entry.getKey())) { + Key key = new Key(apiCollectionId, entry.getKey().getUrl(), entry.getKey().getMethod(), -1, 0, 0); + sampleData.add(new SampleData(key, entry.getValue().removeAllSampleMessage())); + } + } + + Map deltaTemplateURLToMethods = currentDelta.getTemplateURLToMethods(); + Map dbTemplateURLToMethods = dbCatalog.getTemplateURLToMethods(); + + for(Map.Entry entry: deltaTemplateURLToMethods.entrySet()) { + if (forceUpdate || !dbTemplateURLToMethods.containsKey(entry.getKey())) { + Key key = new Key(apiCollectionId, entry.getKey().getTemplateString(), entry.getKey().getMethod(), -1, 0, 0); + sampleData.add(new SampleData(key, entry.getValue().removeAllSampleMessage())); + } + } + + ArrayList> bulkUpdates = new ArrayList<>(); + for (SampleData sample: sampleData) { + if (sample.getSamples().size() == 0) { + continue; + } + List finalSamples = new ArrayList<>(); + for (String s: sample.getSamples()) { + boolean finalRedact = redactSampleData; + if (finalRedact) { + try { + HttpResponseParams httpResponseParams = HttpCallParser.parseKafkaMessage(s); + Source source = httpResponseParams.getSource(); + if (source.equals(Source.HAR) || source.equals(Source.PCAP)) finalRedact = false; + } catch (Exception e1) { + e1.printStackTrace(); + continue; + } + } + + try { + if (finalRedact) { + String redact = RedactSampleData.redact(s); + finalSamples.add(redact); + } else { + finalSamples.add(s); + } + } catch (Exception e) { + ; + } + } + Bson bson = Updates.pushEach("samples", finalSamples, new PushOptions().slice(-10)); + + bulkUpdates.add( + new UpdateOneModel<>(Filters.eq("_id", sample.getId()), bson, new UpdateOptions().upsert(true)) + ); + } + + return bulkUpdates; + } + + + + + public ArrayList> getDBUpdatesForTraffic(int apiCollectionId, APICatalog currentDelta) { + + List trafficInfos = new ArrayList<>(); + for(Map.Entry entry: currentDelta.getStrictURLToMethods().entrySet()) { + trafficInfos.addAll(entry.getValue().removeAllTrafficInfo(apiCollectionId, entry.getKey().getUrl(), entry.getKey().getMethod(), -1)); + } + + for(Map.Entry entry: currentDelta.getTemplateURLToMethods().entrySet()) { + trafficInfos.addAll(entry.getValue().removeAllTrafficInfo(apiCollectionId, entry.getKey().getTemplateString(), entry.getKey().getMethod(), -1)); + } + + ArrayList> bulkUpdates = new ArrayList<>(); + for (TrafficInfo trafficInfo: trafficInfos) { + List updates = new ArrayList<>(); + + for (Map.Entry entry: trafficInfo.mapHoursToCount.entrySet()) { + updates.add(Updates.inc("mapHoursToCount."+entry.getKey(), entry.getValue())); + } + + bulkUpdates.add( + new UpdateOneModel<>(Filters.eq("_id", trafficInfo.getId()), Updates.combine(updates), new UpdateOptions().upsert(true)) + ); + } + + return bulkUpdates; + } + + public DbUpdateReturn getDBUpdatesForParams(APICatalog currentDelta, APICatalog currentState, boolean redactSampleData) { + Map dbInfoMap = convertToMap(currentState.getAllTypeInfo()); + Map deltaInfoMap = convertToMap(currentDelta.getAllTypeInfo()); + + ArrayList> bulkUpdates = new ArrayList<>(); + ArrayList> bulkUpdatesForSampleData = new ArrayList<>(); + int now = Context.now(); + for(String key: deltaInfoMap.keySet()) { + SingleTypeInfo dbInfo = dbInfoMap.get(key); + SingleTypeInfo deltaInfo = deltaInfoMap.get(key); + Bson update; + + int inc = deltaInfo.getCount() - (dbInfo == null ? 0 : dbInfo.getCount()); + long lastSeenDiff = deltaInfo.getLastSeen() - (dbInfo == null ? 0 : dbInfo.getLastSeen()); + boolean minMaxChanged = (dbInfo == null) || (dbInfo.getMinValue() != deltaInfo.getMinValue()) || (dbInfo.getMaxValue() != deltaInfo.getMaxValue()); + boolean valuesChanged = (dbInfo == null) || (dbInfo.getValues().count() != deltaInfo.getValues().count()); + + if (inc == 0 && lastSeenDiff < (60*30) && !minMaxChanged && !valuesChanged) { + continue; + } else { + inc = 1; + } + + int oldTs = dbInfo == null ? 0 : dbInfo.getTimestamp(); + + update = Updates.inc("count", inc); + + if (oldTs == 0) { + update = Updates.combine(update, Updates.set("timestamp", now)); + } + + update = Updates.combine(update, Updates.max(SingleTypeInfo.LAST_SEEN, deltaInfo.getLastSeen())); + update = Updates.combine(update, Updates.max(SingleTypeInfo.MAX_VALUE, deltaInfo.getMaxValue())); + update = Updates.combine(update, Updates.min(SingleTypeInfo.MIN_VALUE, deltaInfo.getMinValue())); + + if (dbInfo != null) { + SingleTypeInfo.Domain domain = dbInfo.getDomain(); + if (domain == SingleTypeInfo.Domain.ENUM) { + CappedSet values = dbInfo.getValues(); + Set elements = new HashSet<>(); + if (values != null) { + elements = values.getElements(); + } + int valuesSize = elements.size(); + if (valuesSize >= SingleTypeInfo.VALUES_LIMIT) { + SingleTypeInfo.Domain newDomain; + if (dbInfo.getSubType().equals(SingleTypeInfo.INTEGER_32) || dbInfo.getSubType().equals(SingleTypeInfo.INTEGER_64) || dbInfo.getSubType().equals(SingleTypeInfo.FLOAT)) { + newDomain = SingleTypeInfo.Domain.RANGE; + } else { + newDomain = SingleTypeInfo.Domain.ANY; + } + update = Updates.combine(update, Updates.set(SingleTypeInfo._DOMAIN, newDomain)); + } + } else { + deltaInfo.setDomain(dbInfo.getDomain()); + deltaInfo.setValues(new CappedSet<>()); + if (!dbInfo.getValues().getElements().isEmpty()) { + Bson bson = Updates.set(SingleTypeInfo._VALUES +".elements",new ArrayList<>()); + update = Updates.combine(update, bson); + } + } + } + + if (dbInfo == null || dbInfo.getDomain() == SingleTypeInfo.Domain.ENUM) { + CappedSet values = deltaInfo.getValues(); + if (values != null) { + Set elements = new HashSet<>(); + for (String el: values.getElements()) { + if (redactSampleData) { + elements.add(el.hashCode()+""); + } else { + elements.add(el); + } + } + Bson bson = Updates.addEachToSet(SingleTypeInfo._VALUES +".elements",new ArrayList<>(elements)); + update = Updates.combine(update, bson); + deltaInfo.setValues(new CappedSet<>()); + } + } + + + if (!redactSampleData && deltaInfo.getExamples() != null && !deltaInfo.getExamples().isEmpty()) { + Bson bson = Updates.pushEach(SensitiveSampleData.SAMPLE_DATA, Arrays.asList(deltaInfo.getExamples().toArray()), new PushOptions().slice(-1 *SensitiveSampleData.cap)); + bulkUpdatesForSampleData.add( + new UpdateOneModel<>( + SensitiveSampleDataDao.getFilters(deltaInfo), + bson, + new UpdateOptions().upsert(true) + ) + ); + } + + Bson updateKey = SingleTypeInfoDao.createFilters(deltaInfo); + + bulkUpdates.add(new UpdateOneModel<>(updateKey, update, new UpdateOptions().upsert(true))); + } + + for(SingleTypeInfo deleted: currentDelta.getDeletedInfo()) { + currentDelta.getStrictURLToMethods().remove(new URLStatic(deleted.getUrl(), Method.fromString(deleted.getMethod()))); + bulkUpdates.add(new DeleteOneModel<>(SingleTypeInfoDao.createFilters(deleted), new DeleteOptions())); + bulkUpdatesForSampleData.add(new DeleteOneModel<>(SensitiveSampleDataDao.getFilters(deleted), new DeleteOptions())); + } + + + ArrayList> bulkUpdatesForSensitiveParamInfo = new ArrayList<>(); + for (SensitiveParamInfo sensitiveParamInfo: sensitiveParamInfoBooleanMap.keySet()) { + if (!sensitiveParamInfoBooleanMap.get(sensitiveParamInfo)) continue; + bulkUpdatesForSensitiveParamInfo.add( + new UpdateOneModel( + SensitiveParamInfoDao.getFilters(sensitiveParamInfo), + Updates.set(SensitiveParamInfo.SAMPLE_DATA_SAVED, true), + new UpdateOptions().upsert(false) + ) + ); + } + + return new DbUpdateReturn(bulkUpdates, bulkUpdatesForSampleData, bulkUpdatesForSensitiveParamInfo); + } + + public static class DbUpdateReturn { + public ArrayList> bulkUpdatesForSingleTypeInfo; + public ArrayList> bulkUpdatesForSampleData; + public ArrayList> bulkUpdatesForSensitiveParamInfo = new ArrayList<>(); + + public DbUpdateReturn(ArrayList> bulkUpdatesForSingleTypeInfo, + ArrayList> bulkUpdatesForSampleData, + ArrayList> bulkUpdatesForSensitiveParamInfo + ) { + this.bulkUpdatesForSingleTypeInfo = bulkUpdatesForSingleTypeInfo; + this.bulkUpdatesForSampleData = bulkUpdatesForSampleData; + this.bulkUpdatesForSensitiveParamInfo = bulkUpdatesForSensitiveParamInfo; + } + } + + + public static String[] trimAndSplit(String url) { + return trim(url).split("/"); + } + + public static URLTemplate createUrlTemplate(String url, Method method) { + String[] tokens = trimAndSplit(url); + SuperType[] types = new SuperType[tokens.length]; + for(int i = 0; i < tokens.length; i ++ ) { + String token = tokens[i]; + + if (token.equals("STRING")) { + tokens[i] = null; + types[i] = SuperType.STRING; + } else if (token.equals("INTEGER")) { + tokens[i] = null; + types[i] = SuperType.INTEGER; + } else { + types[i] = null; + } + + } + + URLTemplate urlTemplate = new URLTemplate(tokens, types, method); + + return urlTemplate; + } + + private int lastMergeAsyncOutsideTs = 0; + public void buildFromDB(boolean calcDiff, boolean fetchAllSTI) { + + if (mergeAsyncOutside) { + if (Context.now() - lastMergeAsyncOutsideTs > 600) { + this.lastMergeAsyncOutsideTs = Context.now(); + + boolean gotDibs = Cluster.callDibs(Cluster.RUNTIME_MERGER, 1800, 60); + if (gotDibs) { + BackwardCompatibility backwardCompatibility = BackwardCompatibilityDao.instance.findOne(new BasicDBObject()); + if (backwardCompatibility.getMergeOnHostInit() == 0) { + new MergeOnHostOnly().mergeHosts(); + Bson update = Updates.set(BackwardCompatibility.MERGE_ON_HOST_INIT, Context.now()); + BackwardCompatibilityDao.instance.getMCollection().updateMany(new BasicDBObject(), update); + } + + try { + List allCollections = ApiCollectionsDao.instance.getMetaAll(); + for(ApiCollection apiCollection: allCollections) { + mergeUrlsAndSave(apiCollection.getId()); + } + } catch (Exception e) { + ; + } + } + } + } + + List allParams; + if (fetchAllSTI) { + allParams = SingleTypeInfoDao.instance.fetchAll(); + } else { + List apiCollectionIds = ApiCollectionsDao.instance.fetchNonTrafficApiCollectionsIds(); + allParams = SingleTypeInfoDao.instance.fetchStiOfCollections(apiCollectionIds); + } + this.dbState = build(allParams); + this.sensitiveParamInfoBooleanMap = new HashMap<>(); + List sensitiveParamInfos = SensitiveParamInfoDao.instance.getUnsavedSensitiveParamInfos(); + for (SensitiveParamInfo sensitiveParamInfo: sensitiveParamInfos) { + this.sensitiveParamInfoBooleanMap.put(sensitiveParamInfo, false); + } + + if (mergeAsyncOutside) { + this.delta = new HashMap<>(); + return; + } + + if(calcDiff) { + for(int collectionId: this.dbState.keySet()) { + APICatalog newCatalog = this.dbState.get(collectionId); + Set newURLs = new HashSet<>(); + for(URLTemplate url: newCatalog.getTemplateURLToMethods().keySet()) { + newURLs.add(url.getTemplateString()+ " "+ url.getMethod().name()); + } + for(URLStatic url: newCatalog.getStrictURLToMethods().keySet()) { + newURLs.add(url.getUrl()+ " "+ url.getMethod().name()); + } + + Bson findQ = Filters.eq("_id", collectionId); + + ApiCollectionsDao.instance.getMCollection().updateOne(findQ, Updates.set("urls", newURLs)); + } + } else { + + for(Map.Entry entry: this.dbState.entrySet()) { + int apiCollectionId = entry.getKey(); + APICatalog apiCatalog = entry.getValue(); + for(URLTemplate urlTemplate: apiCatalog.getTemplateURLToMethods().keySet()) { + Iterator> staticURLIterator = apiCatalog.getStrictURLToMethods().entrySet().iterator(); + while(staticURLIterator.hasNext()){ + Map.Entry urlXTemplate = staticURLIterator.next(); + URLStatic urlStatic = urlXTemplate.getKey(); + RequestTemplate requestTemplate = urlXTemplate.getValue(); + if (urlTemplate.match(urlStatic)) { + if (this.delta == null) { + this.delta = new HashMap<>(); + } + + if (this.getDelta(apiCollectionId) == null) { + this.delta.put(apiCollectionId, new APICatalog(apiCollectionId, new HashMap<>(), new HashMap<>())); + } + + this.getDelta(apiCollectionId).getDeletedInfo().addAll(requestTemplate.getAllTypeInfo()); + staticURLIterator.remove(); + } + } + } + } + } + } + + + private static Map build(List allParams) { + Map ret = new HashMap<>(); + + for (SingleTypeInfo param: allParams) { + String url = param.getUrl(); + int collId = param.getApiCollectionId(); + APICatalog catalog = ret.get(collId); + + if (catalog == null) { + catalog = new APICatalog(collId, new HashMap<>(), new HashMap<>()); + ret.put(collId, catalog); + } + RequestTemplate reqTemplate; + if (APICatalog.isTemplateUrl(url)) { + URLTemplate urlTemplate = createUrlTemplate(url, Method.valueOf(param.getMethod())); + reqTemplate = catalog.getTemplateURLToMethods().get(urlTemplate); + + if (reqTemplate == null) { + reqTemplate = new RequestTemplate(new HashMap<>(), new HashMap<>(), new HashMap<>(), new TrafficRecorder(new HashMap<>())); + catalog.getTemplateURLToMethods().put(urlTemplate, reqTemplate); + } + + } else { + URLStatic urlStatic = new URLStatic(url, Method.fromString(param.getMethod())); + reqTemplate = catalog.getStrictURLToMethods().get(urlStatic); + if (reqTemplate == null) { + reqTemplate = new RequestTemplate(new HashMap<>(), new HashMap<>(), new HashMap<>(), new TrafficRecorder(new HashMap<>())); + catalog.getStrictURLToMethods().put(urlStatic, reqTemplate); + } + } + + if (param.getIsUrlParam()) { + Map urlParams = reqTemplate.getUrlParams(); + if (urlParams == null) { + urlParams = new HashMap<>(); + reqTemplate.setUrlParams(urlParams); + } + + String p = param.getParam(); + try { + int position = Integer.parseInt(p); + KeyTypes keyTypes = urlParams.get(position); + if (keyTypes == null) { + keyTypes = new KeyTypes(new HashMap<>(), false); + urlParams.put(position, keyTypes); + } + keyTypes.getOccurrences().put(param.getSubType(), param); + } catch (Exception e) { + logger.error("ERROR while parsing url param position: " + p); + } + continue; + } + + if (param.getResponseCode() > 0) { + RequestTemplate respTemplate = reqTemplate.getResponseTemplates().get(param.getResponseCode()); + if (respTemplate == null) { + respTemplate = new RequestTemplate(new HashMap<>(), new HashMap<>(), new HashMap<>(), new TrafficRecorder(new HashMap<>())); + reqTemplate.getResponseTemplates().put(param.getResponseCode(), respTemplate); + } + + reqTemplate = respTemplate; + } + + Map keyTypesMap = param.getIsHeader() ? reqTemplate.getHeaders() : reqTemplate.getParameters(); + KeyTypes keyTypes = keyTypesMap.get(param.getParam()); + + if (keyTypes == null) { + keyTypes = new KeyTypes(new HashMap<>(), false); + + if (param.getParam() == null) { + logger.info("null value - " + param.composeKey()); + } + + keyTypesMap.put(param.getParam(), keyTypes); + } + + SingleTypeInfo info = keyTypes.getOccurrences().get(param.getSubType()); + if (info != null && info.getTimestamp() > param.getTimestamp()) { + param = info; + } + + keyTypes.getOccurrences().put(param.getSubType(), param); + } + + return ret; + } + + int counter = 0; + + public void syncWithDB(boolean syncImmediately, boolean fetchAllSTI) { + List> writesForParams = new ArrayList<>(); + List> writesForSensitiveSampleData = new ArrayList<>(); + List> writesForTraffic = new ArrayList<>(); + List> writesForSampleData = new ArrayList<>(); + List> writesForSensitiveParamInfo = new ArrayList<>(); + + AccountSettings accountSettings = AccountSettingsDao.instance.findOne(AccountSettingsDao.generateFilter()); + + boolean redact = false; + if (accountSettings != null) { + redact = accountSettings.isRedactPayload(); + } + + counter++; + for(int apiCollectionId: this.delta.keySet()) { + APICatalog deltaCatalog = this.delta.get(apiCollectionId); + APICatalog dbCatalog = this.dbState.getOrDefault(apiCollectionId, new APICatalog(apiCollectionId, new HashMap<>(), new HashMap<>())); + DbUpdateReturn dbUpdateReturn = getDBUpdatesForParams(deltaCatalog, dbCatalog, redact); + writesForParams.addAll(dbUpdateReturn.bulkUpdatesForSingleTypeInfo); + writesForSensitiveSampleData.addAll(dbUpdateReturn.bulkUpdatesForSampleData); + writesForSensitiveParamInfo.addAll(dbUpdateReturn.bulkUpdatesForSensitiveParamInfo); + writesForTraffic.addAll(getDBUpdatesForTraffic(apiCollectionId, deltaCatalog)); + deltaCatalog.setDeletedInfo(new ArrayList<>()); + + boolean forceUpdate = syncImmediately || counter % 10 == 0; + writesForSampleData.addAll(getDBUpdatesForSampleData(apiCollectionId, deltaCatalog,dbCatalog, redact, forceUpdate)); + } + + logger.info("adding " + writesForParams.size() + " updates for params"); + + long start = System.currentTimeMillis(); + + if (writesForParams.size() >0) { + + BulkWriteResult res = + SingleTypeInfoDao.instance.getMCollection().bulkWrite( + writesForParams + ); + + logger.info((System.currentTimeMillis() - start) + ": " + res.getInserts().size() + " " +res.getUpserts().size()); + } + + logger.info("adding " + writesForTraffic.size() + " updates for traffic"); + if(writesForTraffic.size() > 0) { + BulkWriteResult res = TrafficInfoDao.instance.getMCollection().bulkWrite(writesForTraffic); + + logger.info(res.getInserts().size() + " " +res.getUpserts().size()); + + } + + + logger.info("adding " + writesForSampleData.size() + " updates for samples"); + if(writesForSampleData.size() > 0) { + BulkWriteResult res = SampleDataDao.instance.getMCollection().bulkWrite(writesForSampleData); + + logger.info(res.getInserts().size() + " " +res.getUpserts().size()); + + } + + if (writesForSensitiveSampleData.size() > 0) { + SensitiveSampleDataDao.instance.getMCollection().bulkWrite(writesForSensitiveSampleData); + } + + if (writesForSensitiveParamInfo.size() > 0) { + SensitiveParamInfoDao.instance.getMCollection().bulkWrite(writesForSensitiveParamInfo); + } + + buildFromDB(true, fetchAllSTI); + } + + public void printNewURLsInDelta(APICatalog deltaCatalog) { + for(URLStatic s: deltaCatalog.getStrictURLToMethods().keySet()) { + logger.info(s.getUrl()); + } + + for(URLTemplate s: deltaCatalog.getTemplateURLToMethods().keySet()) { + logger.info(s.getTemplateString()); + } + } + + + public APICatalog getDelta(int apiCollectionId) { + return this.delta.get(apiCollectionId); + } + + + public APICatalog getDbState(int apiCollectionId) { + return this.dbState.get(apiCollectionId); + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/ConcurrentHashSet.java b/apps/api-runtime/src/main/java/com/akto/runtime/ConcurrentHashSet.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/Flow.java b/apps/api-runtime/src/main/java/com/akto/runtime/Flow.java new file mode 100644 index 0000000000..621f21ff57 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/Flow.java @@ -0,0 +1,64 @@ +package com.akto.runtime; + +import com.akto.DaoInit; +import com.akto.dao.RelationshipDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.context.Context; +import com.akto.dto.Markov; +import com.akto.dto.Relationship; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.parsers.HttpCallParser; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import org.bson.conversions.Bson; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.HttpRequestParams; + + +import java.io.*; +import java.util.*; + +public class Flow { + MarkovSync markovSync; + RelationshipSync relationshipSync; + String userIdentifierName; + + public Flow(int relationship_user_thresh, int relationship_counter_thresh, int relationship_last_sync_thresh, + int markov_user_thresh, int markov_counter_thresh, int markov_last_sync_thresh, String userIdentifierName) { + this.userIdentifierName = userIdentifierName; + this.markovSync = new MarkovSync(markov_user_thresh ,markov_counter_thresh, markov_last_sync_thresh); + this.relationshipSync = new RelationshipSync(relationship_user_thresh, relationship_counter_thresh, + relationship_last_sync_thresh); + } + + public void init(List httpResponseParams) throws Exception{ + markovSync.buildMarkov(httpResponseParams, userIdentifierName); + relationshipSync.init(httpResponseParams, userIdentifierName); + } + + public static String getUserIdentifier(String name, HttpRequestParams requestParams) throws Exception { + Map> headers = requestParams.getHeaders(); + List l = headers.get(name); + if (l == null) throw new Exception("Header doesn't exist"); + if (l.size() < 1) throw new Exception("User identifier headers list is empty"); + String userIdentifier = l.get(0); + if (userIdentifier != null) return userIdentifier; + throw new Exception("User identifier not found"); + } + + public static String calculateTodayKey() { + int date = Context.convertEpochToDateInt(Context.now(), "UTC"); + return date + ""; + } + + + + + +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/KafkaHealthMetricSyncTask.java b/apps/api-runtime/src/main/java/com/akto/runtime/KafkaHealthMetricSyncTask.java new file mode 100644 index 0000000000..1988b2dddc --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/KafkaHealthMetricSyncTask.java @@ -0,0 +1,62 @@ +package com.akto.runtime; + +import com.akto.dao.KafkaHealthMetricsDao; +import com.akto.dao.context.Context; +import com.akto.dto.KafkaHealthMetric; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.ReplaceOptions; + +import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.common.TopicPartition; +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class KafkaHealthMetricSyncTask implements Runnable{ + Consumer consumer; + public Map kafkaHealthMetricsMap = new HashMap<>(); + private static final Logger logger = LoggerFactory.getLogger(KafkaHealthMetricSyncTask.class); + + + public KafkaHealthMetricSyncTask(Consumer consumer) { + this.consumer = consumer; + } + + + @Override + public void run() { + try { + logger.info("SYNCING"); + for (TopicPartition tp: consumer.assignment()) { + String tpName = tp.topic(); + long position = consumer.position(tp); + long endOffset = consumer.endOffsets(Collections.singleton(tp)).get(tp); + int partition = tp.partition(); + + KafkaHealthMetric kafkaHealthMetric = new KafkaHealthMetric(tpName, partition, + position,endOffset,Context.now()); + kafkaHealthMetricsMap.put(kafkaHealthMetric.hashCode()+"", kafkaHealthMetric); + } + + for (String key: kafkaHealthMetricsMap.keySet()) { + KafkaHealthMetric kafkaHealthMetric = kafkaHealthMetricsMap.get(key); + Bson filter = Filters.and( + Filters.eq(KafkaHealthMetric.TOPIC_NAME, kafkaHealthMetric.getTopicName()), + Filters.eq(KafkaHealthMetric.PARTITION, kafkaHealthMetric.getPartition()) + ); + + ReplaceOptions replaceOptions = new ReplaceOptions(); + replaceOptions.upsert(true); + + KafkaHealthMetricsDao.instance.getMCollection().replaceOne(filter, kafkaHealthMetric, replaceOptions); + } + logger.info("SYNC DONE"); + } catch (Exception e) { + logger.error("ERROR in kafka data sync from api runtime", e); + } + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/Main.java b/apps/api-runtime/src/main/java/com/akto/runtime/Main.java new file mode 100644 index 0000000000..85596d9723 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/Main.java @@ -0,0 +1,328 @@ +package com.akto.runtime; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.akto.DaoInit; +import com.akto.dao.*; +import com.akto.dao.context.Context; +import com.akto.dto.APIConfig; +import com.akto.dto.AccountSettings; +import com.akto.dto.ApiCollection; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.kafka.Kafka; +import com.akto.parsers.HttpCallParser; +import com.akto.dto.HttpResponseParams; +import com.akto.runtime.policies.AktoPolicies; +import com.google.gson.Gson; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.Updates; + +import org.apache.kafka.clients.consumer.*; +import org.apache.kafka.common.errors.WakeupException; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main { + private Consumer consumer; + public static final String GROUP_NAME = "group_name"; + public static final String VXLAN_ID = "vxlanId"; + public static final String VPC_CIDR = "vpc_cidr"; + private static final Logger logger = LoggerFactory.getLogger(HttpCallParser.class); + + // this sync threshold time is used for deleting sample data + public static final int sync_threshold_time = 120; + + private static int debugPrintCounter = 500; + private static void printL(Object o) { + if (debugPrintCounter > 0) { + debugPrintCounter--; + logger.info(o.toString()); + } + } + + public static boolean tryForCollectionName(String message) { + boolean ret = false; + try { + Gson gson = new Gson(); + + Map json = gson.fromJson(message, Map.class); + + // logger.info("Json size: " + json.size()); + boolean withoutCidrCond = json.size() == 2 && json.containsKey(GROUP_NAME) && json.containsKey(VXLAN_ID); + boolean withCidrCond = json.size() == 3 && json.containsKey(GROUP_NAME) && json.containsKey(VXLAN_ID) && json.containsKey(VPC_CIDR); + if (withCidrCond || withoutCidrCond) { + ret = true; + String groupName = (String) (json.get(GROUP_NAME)); + String vxlanIdStr = ((Double) json.get(VXLAN_ID)).intValue() + ""; + int vxlanId = Integer.parseInt(vxlanIdStr); + ApiCollectionsDao.instance.getMCollection().updateMany( + Filters.eq(ApiCollection.VXLAN_ID, vxlanId), + Updates.set(ApiCollection.NAME, groupName) + ); + + if (json.size() == 3) { + List cidrList = (List) json.get(VPC_CIDR); + logger.info("cidrList: " + cidrList); + AccountSettingsDao.instance.getMCollection().updateOne( + AccountSettingsDao.generateFilter(), Updates.addEachToSet("privateCidrList", cidrList), new UpdateOptions().upsert(true) + ); + } + } + } catch (Exception e) { + logger.error("error in try collection", e); + } + + return ret; + } + + + public static void createIndices() { + SingleTypeInfoDao.instance.createIndicesIfAbsent(); + SensitiveSampleDataDao.instance.createIndicesIfAbsent(); + SampleDataDao.instance.createIndicesIfAbsent(); + } + + public static void insertRuntimeFilters() { + RuntimeFilterDao.instance.initialiseFilters(); + } + + public static Kafka kafkaProducer = null; + private static void buildKafka(int accountId) { + Context.accountId.set(accountId); + AccountSettings accountSettings = AccountSettingsDao.instance.findOne(AccountSettingsDao.generateFilter(accountId)); + if (accountSettings != null && accountSettings.getCentralKafkaIp()!= null) { + String centralKafkaBrokerUrl = accountSettings.getCentralKafkaIp(); + int centralKafkaBatchSize = AccountSettings.DEFAULT_CENTRAL_KAFKA_BATCH_SIZE; + int centralKafkaLingerMS = AccountSettings.DEFAULT_CENTRAL_KAFKA_LINGER_MS; + if (centralKafkaBrokerUrl != null) { + kafkaProducer = new Kafka(centralKafkaBrokerUrl, centralKafkaLingerMS, centralKafkaBatchSize); + logger.info("Connected to central kafka @ " + Context.now()); + } + } + } + + public static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); + + // REFERENCE: https://www.oreilly.com/library/view/kafka-the-definitive/9781491936153/ch04.html (But how do we Exit?) + public static void main(String[] args) { + String mongoURI = System.getenv("AKTO_MONGO_CONN");; + String configName = System.getenv("AKTO_CONFIG_NAME"); + String topicName = System.getenv("AKTO_KAFKA_TOPIC_NAME"); + String kafkaBrokerUrl = "kafka1:19092"; //System.getenv("AKTO_KAFKA_BROKER_URL"); + String groupIdConfig = System.getenv("AKTO_KAFKA_GROUP_ID_CONFIG"); + String instanceType = System.getenv("AKTO_INSTANCE_TYPE"); + boolean syncImmediately = false; + boolean fetchAllSTI = true; + if (instanceType != null && instanceType.equals("DASHBOARD")) { + syncImmediately = true; + fetchAllSTI = false; + } + int maxPollRecordsConfig = Integer.parseInt(System.getenv("AKTO_KAFKA_MAX_POLL_RECORDS_CONFIG")); + + if (topicName == null) topicName = "akto.api.logs"; + + // mongoURI = "mongodb://write_ops:write_ops@cluster0-shard-00-00.yg43a.mongodb.net:27017,cluster0-shard-00-01.yg43a.mongodb.net:27017,cluster0-shard-00-02.yg43a.mongodb.net:27017/myFirstDatabase?ssl=true&replicaSet=atlas-qd3mle-shard-0&authSource=admin&retryWrites=true&w=majority"; + DaoInit.init(new ConnectionString(mongoURI)); + Context.accountId.set(1_000_000); + initializeRuntime(); + + String centralKafkaTopicName = AccountSettings.DEFAULT_CENTRAL_KAFKA_TOPIC_NAME; + + int accountIdHardcoded = Context.accountId.get(); + buildKafka(accountIdHardcoded); + scheduler.scheduleAtFixedRate(new Runnable() { + public void run() { + if (kafkaProducer == null || !kafkaProducer.producerReady) { + buildKafka(accountIdHardcoded); + } + } + }, 5, 5, TimeUnit.MINUTES); + + + APIConfig apiConfig; + apiConfig = APIConfigsDao.instance.findOne(Filters.eq("name", configName)); + if (apiConfig == null) { + apiConfig = new APIConfig(configName,"access-token", 1, 10_000_000, sync_threshold_time); // this sync threshold time is used for deleting sample data + } + + final Main main = new Main(); + Properties properties = main.configProperties(kafkaBrokerUrl, groupIdConfig, maxPollRecordsConfig); + main.consumer = new KafkaConsumer<>(properties); + + final Thread mainThread = Thread.currentThread(); + + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + main.consumer.wakeup(); + try { + mainThread.join(); + } catch (InterruptedException e) { + ; + } + } + }); + + Map httpCallParserMap = new HashMap<>(); + Map flowMap = new HashMap<>(); + Map aktoPolicyMap = new HashMap<>(); + + // sync infra metrics thread + // ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + // KafkaHealthMetricSyncTask task = new KafkaHealthMetricSyncTask(main.consumer); + // executor.scheduleAtFixedRate(task, 2, 60, TimeUnit.SECONDS); + + long lastSyncOffset = 0; + + try { + main.consumer.subscribe(Arrays.asList(topicName, "har_"+topicName)); + while (true) { + ConsumerRecords records = main.consumer.poll(Duration.ofMillis(10000)); + main.consumer.commitSync(); + + Map> responseParamsToAccountMap = new HashMap<>(); + for (ConsumerRecord r: records) { + HttpResponseParams httpResponseParams; + try { + + printL(r.value()); + lastSyncOffset++; + + if (lastSyncOffset % 100 == 0) { + logger.info("Committing offset at position: " + lastSyncOffset); + } + + if (tryForCollectionName(r.value())) { + continue; + } + + httpResponseParams = HttpCallParser.parseKafkaMessage(r.value()); + + } catch (Exception e) { + logger.error("Error while parsing kafka message " + e); + continue; + } + String accountId = httpResponseParams.getAccountId(); + if (!responseParamsToAccountMap.containsKey(accountId)) { + responseParamsToAccountMap.put(accountId, new ArrayList<>()); + } + responseParamsToAccountMap.get(accountId).add(httpResponseParams); + } + + for (String accountId: responseParamsToAccountMap.keySet()) { + int accountIdInt; + try { + accountIdInt = Integer.parseInt(accountId); + } catch (Exception ignored) { + logger.info("Account id not string"); + continue; + } + + Context.accountId.set(accountIdInt); + + if (!httpCallParserMap.containsKey(accountId)) { + HttpCallParser parser = new HttpCallParser( + apiConfig.getUserIdentifier(), apiConfig.getThreshold(), apiConfig.getSync_threshold_count(), + apiConfig.getSync_threshold_time(), fetchAllSTI + ); + + httpCallParserMap.put(accountId, parser); + } + + if (!flowMap.containsKey(accountId)) { + Flow flow= new Flow( + apiConfig.getThreshold(), apiConfig.getSync_threshold_count(), apiConfig.getSync_threshold_time(), + apiConfig.getThreshold(), apiConfig.getSync_threshold_count(), apiConfig.getSync_threshold_time(), + apiConfig.getUserIdentifier() + ); + + flowMap.put(accountId, flow); + } + + if (!aktoPolicyMap.containsKey(accountId)) { + APICatalogSync apiCatalogSync = httpCallParserMap.get(accountId).apiCatalogSync; + AktoPolicies aktoPolicy = new AktoPolicies(apiCatalogSync, fetchAllSTI); + aktoPolicyMap.put(accountId, aktoPolicy); + } + + HttpCallParser parser = httpCallParserMap.get(accountId); + // Flow flow = flowMap.get(accountId); + AktoPolicies aktoPolicy = aktoPolicyMap.get(accountId); + + try { + List accWiseResponse = responseParamsToAccountMap.get(accountId); + APICatalogSync apiCatalogSync = parser.syncFunction(accWiseResponse, syncImmediately, fetchAllSTI); + + // send to central kafka + if (kafkaProducer != null) { + for (HttpResponseParams httpResponseParams: accWiseResponse) { + try { + kafkaProducer.send(httpResponseParams.getOrig(), centralKafkaTopicName); + } catch (Exception e) { + // force close it + kafkaProducer.close(); + logger.error(e.getMessage()); + } + } + } + + // flow.init(accWiseResponse); + aktoPolicy.main(accWiseResponse, apiCatalogSync, fetchAllSTI); + } catch (Exception e) { + logger.error(e.toString()); + } + } + } + + } catch (WakeupException ignored) { + // nothing to catch. This exception is called from the shutdown hook. + } catch (Exception e) { + printL(e); + ; + } finally { + main.consumer.close(); + } + } + + public static void initializeRuntime(){ + SingleTypeInfoDao.instance.getMCollection().updateMany(Filters.exists("apiCollectionId", false), Updates.set("apiCollectionId", 0)); + SingleTypeInfo.init(); + + createIndices(); + insertRuntimeFilters(); + try { + AccountSettingsDao.instance.updateVersion(AccountSettings.API_RUNTIME_VERSION); + } catch (Exception e) { + logger.error("error while updating dashboard version: " + e.getMessage()); + } + + ApiCollection apiCollection = ApiCollectionsDao.instance.findOne("_id", 0); + if (apiCollection == null) { + Set urls = new HashSet<>(); + for(SingleTypeInfo singleTypeInfo: SingleTypeInfoDao.instance.fetchAll()) { + urls.add(singleTypeInfo.getUrl()); + } + ApiCollectionsDao.instance.insertOne(new ApiCollection(0, "Default", Context.now(), urls, null, 0)); + } + } + + + public static Properties configProperties(String kafkaBrokerUrl, String groupIdConfig, int maxPollRecordsConfig) { + Properties properties = new Properties(); + properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaBrokerUrl); + properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecordsConfig); + properties.put(ConsumerConfig.GROUP_ID_CONFIG, groupIdConfig); + properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); + + return properties; + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/MarkovSync.java b/apps/api-runtime/src/main/java/com/akto/runtime/MarkovSync.java new file mode 100644 index 0000000000..f0ba31190e --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/MarkovSync.java @@ -0,0 +1,151 @@ +package com.akto.runtime; + +import com.akto.dao.MarkovDao; +import com.akto.dao.context.Context; +import com.akto.dto.HttpRequestParams; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.Markov; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.*; +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +import static com.akto.runtime.Flow.getUserIdentifier; + +public class MarkovSync { + public Map> markovFromDb = new HashMap<>(); + public final Map markovMap = new HashMap<>(); + private final Map userLastState = new HashMap<>(); + + private int counter; + private final int counter_thresh; + private int last_sync; + private final int last_sync_thresh; + private final int user_thresh; + + private static final Logger logger = LoggerFactory.getLogger(MarkovSync.class); + + public MarkovSync(int user_thresh,int counter_thresh, int last_sync_thresh) { + this.last_sync = Context.now(); + this.counter = 0; + this.counter_thresh = counter_thresh; + this.last_sync_thresh = last_sync_thresh; + this.user_thresh = user_thresh; + } + + private void buildFromDb() { + markovFromDb = new HashMap<>(); + List markovList = MarkovDao.instance.findAll(new BasicDBObject()); + buildMarkovFromDb(markovList); + } + + public void buildMarkovFromDb(List markovList) { + for (Markov markov: markovList) { + Markov.State curr = markov.getCurrent(); + Markov.State next = markov.getNext(); + int count = markov.getTotalCount(); + + String key = curr.hashCode() + ""; + if (!markovFromDb.containsKey(key)) { + markovFromDb.put(key, new HashMap<>()); + } + markovFromDb.get(key).put(next.hashCode()+"", count); + } + } + + public List> getBulkUpdates() { + List> bulkUpdates = new ArrayList<>(); + String todayKey = Flow.calculateTodayKey(); + + for (String key: markovMap.keySet()) { + Markov value = markovMap.get(key); + if (value.getUserIds().size() < user_thresh) { + continue; + } + + Bson filters = Filters.and( + Filters.eq("current.url", value.getCurrent().getUrl()), + Filters.eq("current.method", value.getCurrent().getMethod()), + Filters.eq("next.url", value.getNext().getUrl()), + Filters.eq("next.method", value.getNext().getMethod()) + ); + + String countFieldName = "countMap." + todayKey; + int count = value.getCountMap().get(todayKey); + Bson update = Updates.inc(countFieldName, count); + + bulkUpdates.add( + new UpdateOneModel<>(filters,update, new UpdateOptions().upsert(true)) + ); + + value.setCountMap(new HashMap<>()); + value.setUserIds(new HashSet<>()); + markovMap.put(key, value); + } + + return bulkUpdates; + } + + private void syncWithDb() { + List> bulkUpdates = getBulkUpdates(); + logger.info("adding " + bulkUpdates.size() + " updates"); + if (bulkUpdates.size() > 0) { + try { + MarkovDao.instance.getMCollection().bulkWrite(bulkUpdates); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + + buildFromDb(); + + } + + public void buildMarkov(List httpResponseParams, String userIdentifierName) { + if (httpResponseParams == null) return; + for (HttpResponseParams httpResponseParam: httpResponseParams) { + HttpRequestParams httpRequestParams = httpResponseParam.getRequestParams(); + Markov.State nextState = new Markov.State(httpRequestParams.getURL(), httpRequestParams.getMethod()); + String userIdentifier; + try { + userIdentifier = getUserIdentifier(userIdentifierName, httpRequestParams); + } catch (Exception e) { + logger.error(e.getMessage()); + continue; + } + this.counter += 1; + + Markov.State currentState = userLastState.get(userIdentifier); + userLastState.put(userIdentifier, nextState); + if (currentState == null) { + continue; + } + + Markov m = new Markov(currentState,nextState,new HashMap<>(), new HashSet<>()); + int hashCode = m.hashCode(); + + Markov value = markovMap.get(hashCode+""); + if (value == null) { + value = m; + } + + value.increaseCount(Flow.calculateTodayKey(), userIdentifier); + markovMap.put(m.hashCode()+"", value); + + } + + if (this.counter >= this.counter_thresh || Context.now() - this.last_sync >= last_sync_thresh) { + syncWithDb(); + this.counter = 0; + this.last_sync = Context.now(); + } + + + } + + + +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/PayloadAnalyzer.java b/apps/api-runtime/src/main/java/com/akto/runtime/PayloadAnalyzer.java new file mode 100644 index 0000000000..48e7167000 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/PayloadAnalyzer.java @@ -0,0 +1,90 @@ +package com.akto.runtime; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.context.Context; +import com.akto.dto.type.EndpointInfo; +import com.akto.dto.type.RequestTemplate; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.SingleTypeInfo.SubType; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.UpdateOneModel; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.Updates; +import com.mongodb.client.model.WriteModel; + +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PayloadAnalyzer { + private static final Logger logger = LoggerFactory.getLogger(PayloadAnalyzer.class); + + private static EndpointInfo endpointInfo = null; + + public static EndpointInfo getEndpointInfo() { + return endpointInfo; + } + + public static void init(int accountId) { + if (endpointInfo == null) { + synchronized(PayloadAnalyzer.class) { + if (endpointInfo == null) { + endpointInfo = new EndpointInfo(new HashMap<>()); + } + } + } + + ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); + scheduledExecutorService.scheduleAtFixedRate(new Runnable(){ + + @Override + public void run() { + Context.accountId.set(accountId); + EndpointInfo temp = endpointInfo; + endpointInfo = new EndpointInfo(new HashMap<>()); + logger.info("gathering updates"); + ArrayList> bulkUpdates = new ArrayList<>(); + + for(Map methodToTemplate: temp.getAllEndpoints().values()) { + + for(RequestTemplate template: methodToTemplate.values()) { + + Set paramNames = template.getParameters().keySet(); + + for(String paramName: paramNames) { + Map occurrences = template.getParameters().get(paramName).getOccurrences(); + for(SubType subType: occurrences.keySet()) { + SingleTypeInfo info = occurrences.get(subType); + Bson update = Updates.inc("count", info.getCount()); + + Bson updateKey = SingleTypeInfoDao.createFilters(info); + + bulkUpdates.add(new UpdateOneModel<>(updateKey, update, new UpdateOptions().upsert(true))); + + } + } + } + + try { + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkUpdates); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + logger.info("updates completed"); + } + + }, 5, 10, TimeUnit.SECONDS); + + logger.info("update service scheduled"); + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/RelationshipSync.java b/apps/api-runtime/src/main/java/com/akto/runtime/RelationshipSync.java new file mode 100644 index 0000000000..fc6c914bab --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/RelationshipSync.java @@ -0,0 +1,258 @@ +package com.akto.runtime; + +import com.akto.dao.RelationshipDao; +import com.akto.dao.context.Context; +import com.akto.dto.Relationship; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.mongodb.client.model.*; +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.HttpRequestParams; + +import java.util.*; + +public class RelationshipSync { + public final Map relations = new HashMap<>(); + private final int user_thresh; + private int counter; + private final int counter_thresh; + private int last_sync; + private final int last_sync_thresh; + public Map>>> userWiseParameterMap = new HashMap<>(); + ObjectMapper mapper = new ObjectMapper(); + JsonFactory factory = mapper.getFactory(); + private static final Logger logger = LoggerFactory.getLogger(RelationshipSync.class); + + public RelationshipSync(int user_thresh, int counter_thresh, int last_sync_thresh) { + this.user_thresh = user_thresh; + this.last_sync = Context.now(); + this.counter = 0; + this.counter_thresh = counter_thresh; + this.last_sync_thresh = last_sync_thresh; + } + + + public void buildRelationships() { + for (String userID: userWiseParameterMap.keySet()) { + Map>> valueMap = userWiseParameterMap.get(userID); + for (String value: valueMap.keySet()) { + Map> b = valueMap.get(value); + Set req = b.get("request"); + Set res = b.get("response"); + + for (Relationship.ApiRelationInfo child: req) { + for (Relationship.ApiRelationInfo parent: res) { + if (!parent.getUrl().equalsIgnoreCase(child.getUrl()) && uniquenessDetermineFunction(value)) { + String key = parent.hashCode() + "." + child.hashCode(); + Relationship relationship = relations.get(key); + if (relationship == null) { + relationship = new Relationship( + parent,child, new HashSet<>(), new HashMap<>() + ); + } + int count = relationship.getCountMap().getOrDefault(Flow.calculateTodayKey(),0); + relationship.getCountMap().put(Flow.calculateTodayKey(), count+1); + relationship.getUserIds().add(userID); + relations.put(key, relationship); + } + } + } + } + } + + + } + + public List> getBulkUpdates() { + List> bulkUpdates = new ArrayList<>(); + String todayKey = Flow.calculateTodayKey(); + + for (String key: relations.keySet()) { + Relationship relation = relations.get(key); + if (relation.getUserIds().size() < user_thresh) { + continue; + } + Bson filters = Filters.and( + Filters.eq("parent.url", relation.getParent().getUrl()), + Filters.eq("parent.method", relation.getParent().getMethod()), + Filters.eq("parent.param", relation.getParent().getParam()), + Filters.eq("parent.isHeader", relation.getParent().isHeader()), + Filters.eq("parent.responseCode", relation.getParent().getResponseCode()), + Filters.eq("child.url", relation.getChild().getUrl()), + Filters.eq("child.method", relation.getChild().getMethod()), + Filters.eq("child.param", relation.getChild().getParam()), + Filters.eq("child.isHeader", relation.getChild().isHeader()), + Filters.eq("child.responseCode", relation.getParent().getResponseCode()) + ); + + String countFieldName = "countMap." + todayKey; + int count = relation.getCountMap().get(todayKey); + Bson update = Updates.inc(countFieldName, count); + + bulkUpdates.add( + new UpdateOneModel<>(filters, update, new UpdateOptions().upsert(true)) + ); + relation.setUserIds(new HashSet<>()); + relation.setCountMap(new HashMap<>()); + } + + return bulkUpdates; + } + + private void syncWithDb() { + List> bulkUpdates = getBulkUpdates(); + logger.info("adding " + bulkUpdates.size() + " updates"); + if (bulkUpdates.size() > 0) { + try { + RelationshipDao.instance.getMCollection().bulkWrite(bulkUpdates); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + + + } + + public static boolean uniquenessDetermineFunction(String value) { + if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) return false; + try { + double a = Double.parseDouble(value); + if (a < 10) return false; + if (a < 100 && a % 10 == 0) return false; + } catch (NumberFormatException ignored) { + + } + return true; + } + + public void init(List httpResponseParams, String userIdentifierName) { + for (HttpResponseParams httpResponseParam: httpResponseParams) { + try { + buildParameterMap(httpResponseParam, userIdentifierName); + } catch (Exception e) { + logger.error(e.getMessage()); + continue; + } + counter += 1; + } + + if (counter >= this.counter_thresh || (Context.now() - this.last_sync >= this.last_sync_thresh) ) { + buildRelationships(); + syncWithDb(); + counter = 0; + last_sync = Context.now(); + userWiseParameterMap = new HashMap<>(); + } + } + + public void buildParameterMap(HttpResponseParams httpResponseParams, String userIdentifierName) throws Exception { + HttpRequestParams httpRequestParams = httpResponseParams.getRequestParams(); + String userIdentifier = Flow.getUserIdentifier(userIdentifierName, httpRequestParams); + + String path = httpRequestParams.getURL(); + String method = httpRequestParams.getMethod(); + int responseCode = httpResponseParams.getStatusCode(); + + Map>> m = userWiseParameterMap.getOrDefault(userIdentifier, new HashMap<>()); + + JsonParser jp = factory.createParser(httpRequestParams.getPayload()); + JsonNode node = mapper.readTree(jp); + Map> requestParamMap = new HashMap<>(); + extractAllValuesFromPayload(node,new ArrayList<>(), requestParamMap); + + + String respPayload = httpResponseParams.getPayload(); + if (respPayload.startsWith("[")) { + respPayload = "{\"json\": "+respPayload+"}"; + } + jp = factory.createParser(respPayload); + node = mapper.readTree(jp); + Map> responseParamMap = new HashMap<>(); + extractAllValuesFromPayload(node,new ArrayList<>(), responseParamMap); + + for (String param: responseParamMap.keySet()) { + Set fieldValueSet = responseParamMap.get(param); + for (String fieldValue: fieldValueSet) { + Map> value = m.get(fieldValue); + if (value == null) { + value = new HashMap<>(); + value.put("request", new HashSet<>()); + value.put("response", new HashSet<>()); + } + + value.get("response").add(new Relationship.ApiRelationInfo(path,method,false,param,responseCode)); + m.put(fieldValue,value); + } + } + + for (String param: requestParamMap.keySet()) { + Set fieldValueSet = requestParamMap.get(param); + for (String fieldValue: fieldValueSet) { + Map> value = m.get(fieldValue); + if (value == null) { + value = new HashMap<>(); + value.put("request", new HashSet<>()); + value.put("response", new HashSet<>()); + } + + value.get("request").add(new Relationship.ApiRelationInfo(path,method,false,param,responseCode)); + m.put(fieldValue,value); + } + } + + userWiseParameterMap.put(userIdentifier,m); + + } + + + public static void extractAllValuesFromPayload(JsonNode node, List params, Map> values) { + if (node == null) return; + if (node.isValueNode()) { + String textValue = node.asText(); + if (textValue != null) { + String param = String.join("",params); + if (param.startsWith("#")) { + param = param.substring(1); + } + if (!values.containsKey(param)) { + values.put(param, new HashSet<>()); + } + values.get(param).add(textValue); + } + } else if (node.isArray()) { + ArrayNode arrayNode = (ArrayNode) node; + for(int i = 0; i < arrayNode.size(); i++) { + JsonNode arrayElement = arrayNode.get(i); + params.add("#$"); + extractAllValuesFromPayload(arrayElement, params, values); + params.remove(params.size()-1); + } + } else { + Iterator fieldNames = node.fieldNames(); + while(fieldNames.hasNext()) { + String fieldName = fieldNames.next(); + params.add("#"+fieldName); + JsonNode fieldValue = node.get(fieldName); + extractAllValuesFromPayload(fieldValue, params,values); + params.remove(params.size()-1); + } + } + + } + + public static boolean checkIfValidText(String text) { + if (text == null) return false; + String trimmedText = text.trim(); + + if (trimmedText.length() == 0) return false; + if (trimmedText.equalsIgnoreCase("null")) return false; + + return true; + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/URLAggregator.java b/apps/api-runtime/src/main/java/com/akto/runtime/URLAggregator.java new file mode 100644 index 0000000000..0543ab2d80 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/URLAggregator.java @@ -0,0 +1,71 @@ +package com.akto.runtime; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.akto.dto.type.URLStatic; +import com.akto.dto.type.URLMethods.Method; +import com.akto.dto.HttpResponseParams; +import com.mongodb.BasicDBObject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class URLAggregator { + + private static final Logger logger = LoggerFactory.getLogger(URLAggregator.class); + + ConcurrentMap> urls; + + public static URLStatic getBaseURL(String url, String method) { + if (url == null) { + return null; + } + + return new URLStatic(url.split("\\?")[0], Method.fromString(method)); + } + + + public URLAggregator() { + this.urls = new ConcurrentHashMap<>(); + } + + public URLAggregator(ConcurrentMap> urls) { + this.urls = urls; + } + + public void addURL(HttpResponseParams responseParams) { + URLStatic url = getBaseURL(responseParams.getRequestParams().getURL(), responseParams.getRequestParams().getMethod()); + + Set responses = urls.get(url); + if (responses == null) { + responses = Collections.newSetFromMap(new ConcurrentHashMap()); + urls.put(url, responses); + } + + responses.add(responseParams); + + } + + public void addURL(Set responseParams, URLStatic url) { + Set responses = urls.get(url); + if (responses == null) { + responses = Collections.newSetFromMap(new ConcurrentHashMap()); + urls.put(url, responses); + } + + responses.addAll(responseParams); + + } + + + public void printPendingURLs() { + for(URLStatic s: urls.keySet()) { + logger.info(s+":"+urls.get(s).size()); + } + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/merge/MergeOnHostOnly.java b/apps/api-runtime/src/main/java/com/akto/runtime/merge/MergeOnHostOnly.java new file mode 100644 index 0000000000..4b79376321 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/merge/MergeOnHostOnly.java @@ -0,0 +1,214 @@ +package com.akto.runtime.merge; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bson.conversions.Bson; + +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.ApiInfoDao; +import com.akto.dao.FilterSampleDataDao; +import com.akto.dao.SampleDataDao; +import com.akto.dao.SensitiveParamInfoDao; +import com.akto.dao.SensitiveSampleDataDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.TrafficInfoDao; +import com.akto.dto.ApiCollection; +import com.akto.dto.ApiInfo; +import com.akto.dto.FilterSampleData; +import com.akto.dto.SensitiveSampleData; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.traffic.TrafficInfo; +import com.akto.dto.type.SingleTypeInfo; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.InsertManyOptions; +import com.mongodb.client.model.Updates; + +public class MergeOnHostOnly { + + /* + * apicollections -> findall and create a map of hostnames -> list of apicollection ids + * make a new collection and add all data in it for api collection (and remove duplicates) + * set the apicollection id as the first one for all the others in the list for + * apiinfos, sampledata, sensitivesampledata, singletypeinfo, trafficInfo + * delete in case the _id exists ( i.e. the apicollection already contains this url+method) + */ + + public MergeOnHostOnly() {} + + public void updateAllCollections(int oldId, int newId) { + + InsertManyOptions options = new InsertManyOptions(); + options.ordered(false); + // this allows to insert regradless of failures, i.e. in case an insert fails, it will move to insert the next one and so on... + + List apiInfos = ApiInfoDao.instance.findAll("_id.apiCollectionId", oldId); + if(apiInfos!=null && apiInfos.size()>0){ + apiInfos.forEach((apiInfo)->apiInfo.getId().setApiCollectionId(newId)); + try{ + ApiInfoDao.instance.getMCollection().insertMany(apiInfos,options); + } catch(Exception e){ + + } + ApiInfoDao.instance.getMCollection().deleteMany(Filters.eq("_id.apiCollectionId", oldId)); + } + + List sampleDatas = SampleDataDao.instance.findAll("_id.apiCollectionId", oldId); + if(sampleDatas!=null && sampleDatas.size()>0){ + sampleDatas.forEach((sampleData)->sampleData.getId().setApiCollectionId(newId)); + try{ + SampleDataDao.instance.getMCollection().insertMany(sampleDatas,options); + } catch(Exception e){ + + } + SampleDataDao.instance.getMCollection().deleteMany(Filters.eq("_id.apiCollectionId", oldId)); + } + + List sensitiveSampleDatas = SensitiveSampleDataDao.instance.findAll("_id.apiCollectionId", oldId); + if(sensitiveSampleDatas!=null && sensitiveSampleDatas.size()>0){ + sensitiveSampleDatas.forEach((sensitiveSampleData)->sensitiveSampleData.getId().setApiCollectionId(newId)); + try{ + SensitiveSampleDataDao.instance.getMCollection().insertMany(sensitiveSampleDatas,options); + } catch(Exception e){ + + } + SensitiveSampleDataDao.instance.getMCollection().deleteMany(Filters.eq("_id.apiCollectionId", oldId)); + } + + List trafficInfos = TrafficInfoDao.instance.findAll("_id.apiCollectionId", oldId); + if(trafficInfos!=null && trafficInfos.size()>0){ + trafficInfos.forEach((trafficInfo)->trafficInfo.getId().setApiCollectionId(newId)); + try{ + TrafficInfoDao.instance.getMCollection().insertMany(trafficInfos,options); + } catch(Exception e){ + + } + TrafficInfoDao.instance.getMCollection().deleteMany(Filters.eq("_id.apiCollectionId", oldId)); + } + + SensitiveParamInfoDao.instance.getMCollection().deleteMany(Filters.eq("apiCollectionId", oldId)); + FilterSampleDataDao.instance.getMCollection().deleteMany(Filters.eq("_id.apiInfoKey.apiCollectionId", oldId)); + } + + public void updateSTI(int oldId, int newId){ + SingleTypeInfoDao.instance.getMCollection().updateMany( + Filters.eq("apiCollectionId",oldId), + Updates.set("apiCollectionId",newId)); + } + + public void deleteFromAllCollections(int apiCollectionId, List urls ) { + + Bson filter = Filters.and( + Filters.eq("_id.apiCollectionId", apiCollectionId), + Filters.in("_id.url", urls)); + + ApiInfoDao.instance.getMCollection().deleteMany(filter); + SampleDataDao.instance.getMCollection().deleteMany(filter); + SensitiveSampleDataDao.instance.getMCollection().deleteMany(filter); + TrafficInfoDao.instance.getMCollection().deleteMany(filter); + + SingleTypeInfoDao.instance.getMCollection().deleteMany( + Filters.and( + Filters.eq("apiCollectionId", apiCollectionId), + Filters.in("url",urls))); + } + + public Set getUrlList(String host, int apiCollectionId){ + Set ret = new HashSet<>(); + + Bson filterQ = SingleTypeInfoDao.filterForHostHeader(apiCollectionId, true); + List singleTypeInfos = SingleTypeInfoDao.instance.findAll(filterQ); + + for(SingleTypeInfo s: singleTypeInfos){ + // urls will be merged without method: might result in some data loss + ret.add(s.getUrl()); + } + return ret; + } + + public void mergeHostUtil(String host, List apiCollectionIds) { + if (apiCollectionIds.size() == 0) return; + + int newApiCollectionId = host.hashCode(); + + if (apiCollectionIds.contains(newApiCollectionId)) { + apiCollectionIds.remove(apiCollectionIds.indexOf(newApiCollectionId)); + } else { + + ApiCollection old = ApiCollectionsDao.instance.findOne("_id", apiCollectionIds.get(0)); + old.setId(newApiCollectionId); + + try { + ApiCollectionsDao.instance.insertOne(new ApiCollection(newApiCollectionId, null, old.getStartTs(), new HashSet<>(), host, 0)); + } catch (Exception e) { + return; + } + + int currOldId = apiCollectionIds.get(0); + + ApiCollectionsDao.instance.getMCollection().deleteOne(Filters.eq( "_id", currOldId)); + + updateSTI(currOldId, newApiCollectionId); + updateAllCollections(currOldId, newApiCollectionId); + + apiCollectionIds.remove(0); + } + + try { + + Set urls = getUrlList(host, newApiCollectionId); + for (int i = 0; i < apiCollectionIds.size(); i++) { + + List urlList = new ArrayList<>(urls); + int sz = urlList.size(); + int j = 0; + int currOldId = apiCollectionIds.get(i); + do { + deleteFromAllCollections(currOldId, urlList.subList(j, Math.min(j + 1000, sz))); + j += 1000; + } while (j < sz); + + urls.addAll(getUrlList(host, currOldId)); + + ApiCollectionsDao.instance.getMCollection().deleteOne(Filters.eq("_id", currOldId)); + updateSTI(currOldId, newApiCollectionId); + updateAllCollections(currOldId, newApiCollectionId); + } + + } catch (Exception e) { + + + } + + } + + public void mergeHosts() { + List apiCollections = ApiCollectionsDao.instance.getMetaAll(); + + Map> hostToApiCollectionId = new HashMap<>(); + + for (ApiCollection it : apiCollections) { + if (it.getHostName() == null){ + continue; + } + + List apiCollectionIds = hostToApiCollectionId.get(it.getHostName()); + + if (apiCollectionIds == null) { + apiCollectionIds = new ArrayList<>(); + hostToApiCollectionId.put(it.getHostName(), apiCollectionIds); + } + + apiCollectionIds.add(it.getId()); + } + + for (String host : hostToApiCollectionId.keySet()) { + mergeHostUtil(host, hostToApiCollectionId.get(host)); + } + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/merge/MergeOnSlash.java b/apps/api-runtime/src/main/java/com/akto/runtime/merge/MergeOnSlash.java new file mode 100644 index 0000000000..882538139f --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/merge/MergeOnSlash.java @@ -0,0 +1,181 @@ +// package com.akto.runtime.merge; + +// import java.util.*; + +// import com.akto.dao.ApiCollectionsDao; +// import com.akto.dao.ApiInfoDao; +// import com.akto.dao.SampleDataDao; +// import com.akto.dao.SensitiveSampleDataDao; +// import com.akto.dao.SingleTypeInfoDao; +// import com.akto.dao.TrafficInfoDao; +// import com.akto.dto.ApiCollection; +// import com.akto.dto.ApiInfo; +// import com.akto.dto.SensitiveSampleData; +// import com.akto.dto.traffic.SampleData; +// import com.akto.dto.traffic.TrafficInfo; +// import com.akto.dto.type.SingleTypeInfo; +// import com.akto.dto.type.SingleTypeInfo.Domain; +// import com.akto.runtime.APICatalogSync; +// import com.akto.types.CappedSet; +// import com.mongodb.client.model.Filters; +// import com.mongodb.client.model.Updates; + +// public class MergeOnSlash { + +// public void removeApiCollectionsDuplicates(int apiCollectionId){ +// ApiCollection apiCollection = ApiCollectionsDao.instance.findOne("_id",apiCollectionId); + +// Set urls=new HashSet<>(); +// for(String url:apiCollection.getUrls()){ +// if(url.startsWith("/") || url.startsWith("http")){ +// urls.add(url); +// } else { +// urls.add("/"+url); +// } +// } +// ApiCollectionsDao.instance.updateOne("_id",apiCollectionId, +// Updates.set("urls",urls) +// ); +// } + +// public void removeSTIDuplicates(int apiCollectionId) { +// List singleTypeInfos = SingleTypeInfoDao.instance.findAll("apiCollectionId", apiCollectionId); + +// for (SingleTypeInfo singleTypeInfo : singleTypeInfos) { +// if (singleTypeInfo.getUrl().startsWith("/") || singleTypeInfo.getUrl().startsWith("http")) { +// continue; +// } + +// String originalUrl = singleTypeInfo.getUrl(); +// String newUrl = "/" + originalUrl; +// SingleTypeInfo newSingleTypeInfo = singleTypeInfo.copy(); +// newSingleTypeInfo.setUrl(newUrl); +// SingleTypeInfo withslash = SingleTypeInfoDao.instance.findOne(SingleTypeInfoDao.createFilters(newSingleTypeInfo)); + +// if (withslash == null) { +// SingleTypeInfoDao.instance.updateOne( +// SingleTypeInfoDao.createFilters(singleTypeInfo), +// Updates.set("url", newUrl)); +// } else { +// SingleTypeInfoDao.instance.deleteAll(SingleTypeInfoDao.createFilters(singleTypeInfo)); +// } + +// if(singleTypeInfo.getIsUrlParam()){ + +// String[] tokens = APICatalogSync.tokenize(newUrl); +// if( tokens[Integer.parseInt(singleTypeInfo.getParam())].equals("INTEGER") || tokens[Integer.parseInt(singleTypeInfo.getParam())].equals("FLOAT") ){ +// continue; +// } + +// singleTypeInfo.setUrl(newUrl); +// SingleTypeInfoDao.instance.updateOne( +// SingleTypeInfoDao.createFilters(singleTypeInfo), +// Updates.set("param", (Integer.parseInt(singleTypeInfo.getParam())+1)+"" )); + +// } +// } +// } + +// public void removeApiInfoDuplicates(int apiCollectionId){ +// List apiInfos = ApiInfoDao.instance.findAll(Filters.eq("_id.apiCollectionId",apiCollectionId)); + +// for(ApiInfo apiInfo:apiInfos){ +// if(apiInfo.getId().getUrl().startsWith("/") || apiInfo.getId().getUrl().startsWith("http")){ +// continue; +// } + +// String originalUrl = apiInfo.getId().getUrl(); +// String newUrl = "/" + originalUrl; +// ApiInfo newApiInfo = apiInfo.copy(); +// newApiInfo.getId().setUrl(newUrl); +// ApiInfo withSlash = ApiInfoDao.instance.findOne(ApiInfoDao.getFilter(newApiInfo.getId())); + +// if(withSlash == null){ +// ApiInfoDao.instance.insertOne(newApiInfo); +// } +// ApiInfoDao.instance.deleteAll(ApiInfoDao.getFilter(apiInfo.getId())); +// } +// } + +// public void removeSampleDataDuplicates(int apiCollectionId){ +// List sampleDatas = SampleDataDao.instance.findAll("_id.apiCollectionId",apiCollectionId); + +// for(SampleData sampleData:sampleDatas){ +// if(sampleData.getId().getUrl().startsWith("/") || sampleData.getId().getUrl().startsWith("http")){ +// continue; +// } + +// String originalUrl = sampleData.getId().getUrl(); +// String newUrl = "/"+originalUrl; +// SampleData newSampleData = sampleData.copy(); +// newSampleData.getId().setUrl(newUrl); +// SampleData withSlash = SampleDataDao.instance.findOne("_id",newSampleData.getId()); + +// if(withSlash==null){ +// SampleDataDao.instance.insertOne(newSampleData); +// } +// SampleDataDao.instance.deleteAll(Filters.eq("_id",sampleData.getId())); +// } +// } + +// public void removeSensitiveSampleDataDuplicates(int apiCollectionId){ +// List sensitiveSampleDatas = SensitiveSampleDataDao.instance.findAll("_id.apiCollectionId",apiCollectionId); + +// for(SensitiveSampleData sensitiveSampleData:sensitiveSampleDatas){ +// if(sensitiveSampleData.getId().getUrl().startsWith("/") || sensitiveSampleData.getId().getUrl().startsWith("http")){ +// continue; +// } + +// String originalUrl = sensitiveSampleData.getId().getUrl(); +// String newUrl = "/"+originalUrl; +// SensitiveSampleData newSensitiveSampleData = sensitiveSampleData.copy(); +// newSensitiveSampleData.getId().setUrl(newUrl); + +// SingleTypeInfo filter = new SingleTypeInfo(sensitiveSampleData.getId(), new HashSet<>() , new HashSet<>(), 0, 0,0, new CappedSet<>() , Domain.ENUM, 0,0); +// SingleTypeInfo newFilter = new SingleTypeInfo(newSensitiveSampleData.getId(), new HashSet<>() , new HashSet<>(), 0, 0,0, new CappedSet<>() , Domain.ENUM, 0,0); +// SensitiveSampleData withSlash = SensitiveSampleDataDao.instance.findOne(SensitiveSampleDataDao.getFilters(newFilter)); + +// if(withSlash==null){ +// SensitiveSampleDataDao.instance.insertOne(newSensitiveSampleData); +// } +// SensitiveSampleDataDao.instance.deleteAll(SensitiveSampleDataDao.getFilters(filter)); +// } +// } + +// public void removeTrafficInfoDuplicates(int apiCollectionId){ +// List trafficInfos = TrafficInfoDao.instance.findAll(Filters.eq("_id.apiCollectionId",apiCollectionId)); + +// for(TrafficInfo trafficInfo:trafficInfos){ +// if(trafficInfo.getId().getUrl().startsWith("/") || trafficInfo.getId().getUrl().startsWith("http")){ +// continue; +// } + +// String originalUrl = trafficInfo.getId().getUrl(); +// String newUrl = "/"+originalUrl; +// TrafficInfo newTrafficInfo = trafficInfo.copy(); +// newTrafficInfo.getId().setUrl(newUrl); +// TrafficInfo withSlash = TrafficInfoDao.instance.findOne("_id",newTrafficInfo.getId()); + +// if(withSlash == null){ +// TrafficInfoDao.instance.insertOne(newTrafficInfo); +// } +// TrafficInfoDao.instance.deleteAll(Filters.eq("_id",trafficInfo.getId())); +// } +// } + +// public void removeDuplicates(int apiCollectionId){ +// /* +// * 1. only "/" exist -> continue +// * 2. only -"/" exist -> convert to "/" +// * 3. both exist -> delete -"/" and keep "/" +// */ + +// removeApiCollectionsDuplicates(apiCollectionId); +// removeApiInfoDuplicates(apiCollectionId); +// removeSensitiveSampleDataDuplicates(apiCollectionId); +// removeSampleDataDuplicates(apiCollectionId); +// removeSTIDuplicates(apiCollectionId); +// removeTrafficInfoDuplicates(apiCollectionId); +// } + +// } diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/merge/MergeSimilarUrls.java b/apps/api-runtime/src/main/java/com/akto/runtime/merge/MergeSimilarUrls.java new file mode 100644 index 0000000000..07a8e443ab --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/merge/MergeSimilarUrls.java @@ -0,0 +1,172 @@ +package com.akto.runtime.merge; + +import java.util.*; + +import com.akto.dao.*; +import com.akto.dto.*; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.traffic.Key; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods.Method; +import com.akto.types.CappedSet; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.UpdateOneModel; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.Updates; +import com.mongodb.client.model.WriteModel; + +import org.apache.commons.lang3.StringUtils; +import org.bson.conversions.Bson; + +public class MergeSimilarUrls { + + public static void mergeSTI(String mergedUrl, Set toMergeUrls, int apiCollectionId, Method method) { + Bson stiFilter = Filters.and( + Filters.eq(SingleTypeInfo._API_COLLECTION_ID, apiCollectionId), + Filters.eq(SingleTypeInfo._METHOD, method), + Filters.in(SingleTypeInfo._URL,toMergeUrls) + ); + List singleTypeInfos = SingleTypeInfoDao.instance.findAll(stiFilter); + if (singleTypeInfos.isEmpty()) return; + + Set singleTypeInfoSet = new HashSet<>(); + List stiResult = new ArrayList<>(); + for (SingleTypeInfo singleTypeInfo: singleTypeInfos) { + singleTypeInfo.setUrl(mergedUrl); + String key = singleTypeInfo.composeKey(); + if (singleTypeInfoSet.contains(key)) continue; + singleTypeInfoSet.add(singleTypeInfo.composeKey()); + stiResult.add(singleTypeInfo); + } + + ArrayList> bulkUpdates = new ArrayList<>(); + for (SingleTypeInfo singleTypeInfo: stiResult) { + Bson filter = SingleTypeInfoDao.createFilters(singleTypeInfo); + Bson update = Updates.set("count", 1); + bulkUpdates.add(new UpdateOneModel<>(filter, update, new UpdateOptions().upsert(true))); + } + + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkUpdates); + SingleTypeInfoDao.instance.deleteAll(stiFilter); + } + + public static void mergeApiInfo(String mergedUrl, Set toMergeUrls, int apiCollectionId, Method method) { + Bson apiInfoFilter = Filters.and( + Filters.eq("_id.apiCollectionId", apiCollectionId), + Filters.eq("_id.method", method.name()), + Filters.in("_id.url", new ArrayList<>(toMergeUrls)) + ); + List apiInfos = ApiInfoDao.instance.findAll(apiInfoFilter); + if (apiInfos.isEmpty()) return; + ApiInfo mainApiInfo = apiInfos.get(0); + mainApiInfo.setId(new ApiInfo.ApiInfoKey(apiCollectionId, mergedUrl, method)); + ApiInfoDao.instance.insertOne(mainApiInfo); + ApiInfoDao.instance.deleteAll(apiInfoFilter); + } + + public static void mergeSampleData(String mergedUrl, Set toMergeUrls, int apiCollectionId, Method method){ + Bson sampleDataFilter = Filters.and( + Filters.eq("_id.apiCollectionId", apiCollectionId), + Filters.eq("_id.method", method.name()), + Filters.in("_id.url", new ArrayList<>(toMergeUrls)) + ); + List sampleDataList = SampleDataDao.instance.findAll(sampleDataFilter); + if (sampleDataList.isEmpty()) return; + SampleData mainSampleData = sampleDataList.get(0); + mainSampleData.setId(new Key(apiCollectionId, mergedUrl,method, -1, 0, 0)); + SampleDataDao.instance.insertOne(mainSampleData); + SampleDataDao.instance.deleteAll(sampleDataFilter); + } + + public static void mergeTrafficInfo(String mergedUrl, Set toMergeUrls, int apiCollectionId, Method method) { + Bson trafficFilter = Filters.and( + Filters.eq("_id.apiCollectionId", apiCollectionId), + Filters.eq("_id.method", method.name()), + Filters.in("_id.url", new ArrayList<>(toMergeUrls)) + ); + TrafficInfoDao.instance.deleteAll(trafficFilter); + } + + public static void mergeSensitiveSampleData(String mergedUrl, Set toMergeUrls, int apiCollectionId, Method method) { + Bson sensitiveSampleDataFilter = Filters.and( + Filters.eq("_id.apiCollectionId", apiCollectionId), + Filters.eq("_id.method", method.name()), + Filters.in("_id.url", new ArrayList<>(toMergeUrls)) + ); + List sensitiveSampleList = SensitiveSampleDataDao.instance.findAll(sensitiveSampleDataFilter); + if (sensitiveSampleList.isEmpty()) return; + Set sensitiveSetSampleDataKeySet = new HashSet<>(); + List resultSensitiveSampleData = new ArrayList<>(); + for (SensitiveSampleData sensitiveSampleData: sensitiveSampleList) { + SingleTypeInfo.ParamId paramId = sensitiveSampleData.getId(); + paramId.setUrl(mergedUrl); + SingleTypeInfo dummy = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 0, 0, 0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + String key = dummy.composeKey(); + if (sensitiveSetSampleDataKeySet.contains(key)) continue; + sensitiveSetSampleDataKeySet.add(key); + resultSensitiveSampleData.add(sensitiveSampleData); + } + SensitiveSampleDataDao.instance.insertMany(resultSensitiveSampleData); + SensitiveSampleDataDao.instance.deleteAll(sensitiveSampleDataFilter); + } + + public static void mergeSensitiveParamInfo(String mergedUrl, Set toMergeUrls, int apiCollectionId, Method method) { + Bson sensitiveParamInfoFilter = Filters.and( + Filters.eq("apiCollectionId", apiCollectionId), + Filters.eq("method", method), + Filters.in("url",toMergeUrls) + ); + List sensitiveParamInfoList = SensitiveParamInfoDao.instance.findAll(sensitiveParamInfoFilter); + if (sensitiveParamInfoList.isEmpty()) return; + Set sensitiveParamInfoKeySet = new HashSet<>(); + List resultSensitiveParamInfo = new ArrayList<>(); + for (SensitiveParamInfo sensitiveParamInfo: sensitiveParamInfoList) { + sensitiveParamInfo.setUrl(mergedUrl); + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId(sensitiveParamInfo.getParam(), sensitiveParamInfo.getMethod(), sensitiveParamInfo.getResponseCode(), sensitiveParamInfo.isIsHeader(), sensitiveParamInfo.getParam(), SingleTypeInfo.GENERIC, apiCollectionId, false); + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 0, 0, 0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + String key = singleTypeInfo.composeKey(); + + if (sensitiveParamInfoKeySet.contains(key)) continue; + sensitiveParamInfoKeySet.add(key); + resultSensitiveParamInfo.add(sensitiveParamInfo); + } + SensitiveParamInfoDao.instance.insertMany(resultSensitiveParamInfo); + SensitiveParamInfoDao.instance.deleteAll(sensitiveParamInfoFilter); + } + + public static void mergeFilterSampleData(String mergedUrl, Set toMergeUrls, int apiCollectionId, Method method) { + Bson filterSampleDataFilter = Filters.and( + Filters.eq("_id.apiInfoKey.apiCollectionId", apiCollectionId), + Filters.eq("_id.apiInfoKey.method",method), + Filters.in("_id.apiInfoKey.url", toMergeUrls) + ); + List filterSampleList = FilterSampleDataDao.instance.findAll(filterSampleDataFilter); + if (filterSampleList.isEmpty()) return; + Set filterSampleDataKeySet = new HashSet<>(); + List resultFilterSampleList = new ArrayList<>(); + for (FilterSampleData filterSampleData: filterSampleList) { + FilterSampleData.FilterKey id = filterSampleData.getId(); + ApiInfo.ApiInfoKey apiInfoKey = id.apiInfoKey; + apiInfoKey.setUrl(mergedUrl); + int filterId = id.filterId; + String key = StringUtils.joinWith("@", apiInfoKey.getApiCollectionId(), apiInfoKey.url, apiInfoKey.method, filterId); + + if (filterSampleDataKeySet.contains(key)) continue; + filterSampleDataKeySet.add(key); + resultFilterSampleList.add(filterSampleData); + } + FilterSampleDataDao.instance.insertMany(resultFilterSampleList); + FilterSampleDataDao.instance.deleteAll(filterSampleDataFilter); + } + + + public static void mergeAndUpdateDb(String mergedUrl, Set toMergeUrls, int apiCollectionId, Method method) { + mergeSTI(mergedUrl, toMergeUrls, apiCollectionId, method); + mergeApiInfo(mergedUrl, toMergeUrls, apiCollectionId, method); + mergeSampleData(mergedUrl, toMergeUrls, apiCollectionId, method); + mergeTrafficInfo(mergedUrl, toMergeUrls, apiCollectionId, method); + mergeSensitiveSampleData(mergedUrl, toMergeUrls, apiCollectionId, method); + mergeSensitiveParamInfo(mergedUrl, toMergeUrls, apiCollectionId, method); + mergeFilterSampleData(mergedUrl, toMergeUrls, apiCollectionId, method); + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/policies/AktoPolicies.java b/apps/api-runtime/src/main/java/com/akto/runtime/policies/AktoPolicies.java new file mode 100644 index 0000000000..e1f271e626 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/policies/AktoPolicies.java @@ -0,0 +1,38 @@ +package com.akto.runtime.policies; + +import java.util.List; + +import com.akto.dto.HttpResponseParams; +import com.akto.runtime.APICatalogSync; + +public class AktoPolicies { + private AktoPolicy aktoPolicy; + private AktoPolicyNew aktoPolicyNew; + + public AktoPolicies(APICatalogSync apiCatalogSync, boolean fetchAllSTI) { + if (APICatalogSync.mergeAsyncOutside) { + this.aktoPolicyNew = new AktoPolicyNew(fetchAllSTI);; + } else { + this.aktoPolicy = new AktoPolicy(apiCatalogSync, fetchAllSTI); + } + } + + + public AktoPolicy getAktoPolicy() { + return this.aktoPolicy; + } + + public AktoPolicyNew getAktoPolicyNew() { + return this.aktoPolicyNew; + } + + public void main(List httpResponseParamsList, APICatalogSync apiCatalogSync, boolean fetchAllSTI) throws Exception { + if (this.aktoPolicy != null) { + this.aktoPolicy.main(httpResponseParamsList, apiCatalogSync, fetchAllSTI); + } + + if (this.aktoPolicyNew != null) { + this.aktoPolicyNew.main(httpResponseParamsList, apiCatalogSync != null, fetchAllSTI); + } + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/policies/AktoPolicy.java b/apps/api-runtime/src/main/java/com/akto/runtime/policies/AktoPolicy.java new file mode 100644 index 0000000000..f1f23dc791 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/policies/AktoPolicy.java @@ -0,0 +1,645 @@ +package com.akto.runtime.policies; + +import com.akto.DaoInit; +import com.akto.dao.*; +import com.akto.dto.*; +import com.akto.dao.context.Context; +import com.akto.dto.runtime_filters.RuntimeFilter; +import com.akto.dto.type.APICatalog; +import com.akto.dto.type.RequestTemplate; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLStatic; +import com.akto.dto.type.URLTemplate; +import com.akto.runtime.APICatalogSync; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.*; +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +public class AktoPolicy { + public static final String TEMPLATE = "template"; + public static final String STRICT = "strict"; + + private List filters = new ArrayList<>(); + private Map apiInfoCatalogMap = new HashMap<>(); + private Map reserveApiInfoMap = new HashMap<>(); + private Map reserveFilterSampleDataMap = new HashMap<>(); + + boolean processCalledAtLeastOnce = false; + ApiAccessTypePolicy apiAccessTypePolicy = new ApiAccessTypePolicy(null); + boolean redact = false; + + private final int batchTimeThreshold = 120; + private int timeSinceLastSync; + private final int batchSizeThreshold = 10_000_000; + private int currentBatchSize = 0; + + private static final Logger logger = LoggerFactory.getLogger(AktoPolicy.class); + + public void fetchFilters() { + this.filters = RuntimeFilterDao.instance.findAll(new BasicDBObject()); + } + + public AktoPolicy(APICatalogSync apiCatalogSync, boolean fetchAllSTI) { + buildFromDb(apiCatalogSync.dbState, fetchAllSTI); + } + + public void buildFromDb(Map delta, boolean fetchAllSTI) { + fetchFilters(); + + AccountSettings accountSettings = AccountSettingsDao.instance.findOne(new BasicDBObject()); + if (accountSettings != null) { + List cidrList = accountSettings.getPrivateCidrList(); + if ( cidrList != null && !cidrList.isEmpty()) { + // logger.info("Found cidr from db"); + apiAccessTypePolicy.setPrivateCidrList(cidrList); + } + + redact = accountSettings.isRedactPayload(); + } + + apiInfoCatalogMap = new HashMap<>(); + if (delta == null) return; + for (Integer collectionId: delta.keySet()) { + ApiInfoCatalog apiInfoCatalog = new ApiInfoCatalog(new HashMap<>(), new HashMap<>(), new ArrayList<>()); + apiInfoCatalogMap.put(collectionId, apiInfoCatalog); + + APICatalog apiCatalog = delta.get(collectionId); + if (apiCatalog == null) continue; + + Map apiStrictURLToMethods = apiCatalog.getStrictURLToMethods(); + Map apiInfoStrictURLToMethods = apiInfoCatalog.getStrictURLToMethods(); + if (apiInfoStrictURLToMethods == null) { + apiInfoStrictURLToMethods = new HashMap<>(); + } + + if (apiStrictURLToMethods != null) { + for (URLStatic urlStatic: apiStrictURLToMethods.keySet()) { + apiInfoStrictURLToMethods.put(urlStatic, new PolicyCatalog(null, new HashMap<>())); + } + } + + Map apiTemplateURLToMethods = apiCatalog.getTemplateURLToMethods(); + Map apiInfoTemplateURLToMethods = apiInfoCatalog.getTemplateURLToMethods(); + if (apiInfoTemplateURLToMethods == null) { + apiInfoTemplateURLToMethods = new HashMap<>(); + } + if (apiTemplateURLToMethods != null) { + for (URLTemplate urlTemplate: apiTemplateURLToMethods.keySet()) { + apiInfoTemplateURLToMethods.put(urlTemplate,new PolicyCatalog(null, new HashMap<>())); + } + } + } + List apiInfoList; + if (fetchAllSTI) { + apiInfoList = ApiInfoDao.instance.findAll(new BasicDBObject()); + } else { + List apiCollectionIds = ApiCollectionsDao.instance.fetchNonTrafficApiCollectionsIds(); + apiInfoList = ApiInfoDao.instance.findAll(Filters.in("_id.apiCollectionId", apiCollectionIds)); + } + for (ApiInfo apiInfo: apiInfoList) { + try { + fillApiInfoInCatalog(apiInfo, true); + } catch (Exception e) { + logger.error(e.getMessage() + " " + e.getCause()); + } + } + + for (ApiInfo apiInfo: reserveApiInfoMap.values()) { + try { + fillApiInfoInCatalog(apiInfo, false); + } catch (Exception e) { + logger.error(e.getMessage() + " " + e.getCause()); + } + } + + List filterSampleDataList = FilterSampleDataDao.instance.findAll(new BasicDBObject()); + for (FilterSampleData filterSampleData: filterSampleDataList) { + try{ + fillFilterSampleDataInCatalog(filterSampleData); + } catch (Exception e) { + logger.error(e.getMessage() + " " + e.getCause()); + } + } + + for (FilterSampleData filterSampleData: reserveFilterSampleDataMap.values()) { + try { + fillFilterSampleDataInCatalog(filterSampleData); + } catch (Exception e) { + logger.error(e.getMessage() + " " + e.getCause()); + } + } + + reserveApiInfoMap = new HashMap<>(); + reserveFilterSampleDataMap = new HashMap<>(); + } + + public void syncWithDb(boolean initialising, Map delta, boolean fetchAllSTI) { + // logger.info("Syncing with db"); + if (!initialising) { + UpdateReturn updateReturn = AktoPolicy.getUpdates(apiInfoCatalogMap); + List> writesForApiInfo = updateReturn.updatesForApiInfo; + List> writesForSampleData = updateReturn.updatesForSampleData; + logger.info("Writing to db: " + "writesForApiInfoSize="+writesForApiInfo.size() + " writesForSampleData="+ writesForSampleData.size()); + try { + if (writesForApiInfo.size() > 0) ApiInfoDao.instance.getMCollection().bulkWrite(writesForApiInfo); + if (!redact && writesForSampleData.size() > 0) FilterSampleDataDao.instance.getMCollection().bulkWrite(writesForSampleData); + } catch (Exception e) { + logger.error(e.toString()); + } + } + + buildFromDb(delta,fetchAllSTI); + } + + public void fillFilterSampleDataInCatalog(FilterSampleData filterSampleData) { + if (filterSampleData == null || filterSampleData.getId() == null) return; + + ApiInfo.ApiInfoKey apiInfoKey = filterSampleData.getId().getApiInfoKey(); + ApiInfoCatalog apiInfoCatalog = apiInfoCatalogMap.get(apiInfoKey.getApiCollectionId()); + if (apiInfoCatalog == null) { + return; + } + Map strictURLToMethods = apiInfoCatalog.getStrictURLToMethods(); + if (strictURLToMethods == null) { + strictURLToMethods = new HashMap<>(); + apiInfoCatalog.setStrictURLToMethods(strictURLToMethods); + } + URLStatic urlStatic = new URLStatic(apiInfoKey.getUrl(), apiInfoKey.getMethod()); + PolicyCatalog policyCatalog = strictURLToMethods.get(urlStatic); + if (policyCatalog != null) { + Map filterSampleDataMap = policyCatalog.getFilterSampleDataMap(); + if (filterSampleDataMap == null) { + filterSampleDataMap = new HashMap<>(); + policyCatalog.setFilterSampleDataMap(filterSampleDataMap); + } + filterSampleDataMap.put(filterSampleData.getId().getFilterId(), filterSampleData); + return; + } + + // there is a bug that /api/books was stored as api/books in db. So lastSeen has to be handled for this + String staticUrl = urlStatic.getUrl(); + staticUrl = APICatalogSync.trim(staticUrl); + URLStatic newUrlStatic = new URLStatic(staticUrl, urlStatic.getMethod()); + policyCatalog = strictURLToMethods.get(newUrlStatic); + if (policyCatalog != null) { + filterSampleData.getId().getApiInfoKey().setUrl(staticUrl); + Map filterSampleDataMap = policyCatalog.getFilterSampleDataMap(); + if (filterSampleDataMap == null) { + filterSampleDataMap =new HashMap<>(); + policyCatalog.setFilterSampleDataMap(filterSampleDataMap); + } + filterSampleDataMap.put(filterSampleData.getId().getFilterId(), filterSampleData); + return; + } + + Map templateURLToMethods = apiInfoCatalog.getTemplateURLToMethods(); + if (templateURLToMethods == null) { + templateURLToMethods = new HashMap<>(); + apiInfoCatalog.setTemplateURLToMethods(templateURLToMethods); + } + for (URLTemplate urlTemplate: templateURLToMethods.keySet()) { + PolicyCatalog templatePolicyCatalog = templateURLToMethods.get(urlTemplate); + if (templatePolicyCatalog == null) continue; + + if (urlTemplate.match(urlStatic)) { + filterSampleData.getId().getApiInfoKey().setUrl(urlTemplate.getTemplateString()); + // merge with existing apiInfo if present + Map filterSampleDataMap = templatePolicyCatalog.getFilterSampleDataMap(); + if (filterSampleDataMap == null) { + filterSampleDataMap = new HashMap<>(); + templatePolicyCatalog.setFilterSampleDataMap(filterSampleDataMap); + } + FilterSampleData filterSampleDataFromMap = filterSampleDataMap.get(filterSampleData.getId().getFilterId()); + if (filterSampleDataFromMap != null) { + filterSampleData.merge(filterSampleDataFromMap); + } + filterSampleDataMap.put(filterSampleData.getId().getFilterId(), filterSampleData); + return; + } + } + } + + public void fillApiInfoInCatalog(ApiInfo apiInfo, boolean shouldDeleteFromDb) { + ApiInfo.ApiInfoKey apiInfoKey = apiInfo.getId(); + ApiInfoCatalog apiInfoCatalog = apiInfoCatalogMap.get(apiInfoKey.getApiCollectionId()); + if (apiInfoCatalog == null) { + return; + } + + List deletedInfo = apiInfoCatalog.getDeletedInfo(); + if (deletedInfo == null) { + deletedInfo = new ArrayList<>(); + apiInfoCatalog.setDeletedInfo(deletedInfo); + } + + Map strictURLToMethods = apiInfoCatalog.getStrictURLToMethods(); + Map templateURLToMethods = apiInfoCatalog.getTemplateURLToMethods(); + URLStatic urlStatic = new URLStatic(apiInfoKey.getUrl(), apiInfoKey.getMethod()); + PolicyCatalog policyCatalog = strictURLToMethods.get(urlStatic); + if (policyCatalog != null) { + policyCatalog.setApiInfo(apiInfo); + return; + } + + // there is a bug that /api/books was stored as api/books in db. So lastSeen has to be handled for this + String staticUrl = urlStatic.getUrl(); + staticUrl = APICatalogSync.trim(staticUrl); + URLStatic newUrlStatic = new URLStatic(staticUrl, urlStatic.getMethod()); + policyCatalog = strictURLToMethods.get(newUrlStatic); + if (policyCatalog != null) { + apiInfo.getId().setUrl(staticUrl); + policyCatalog.setApiInfo(apiInfo); + return; + } + + for (URLTemplate urlTemplate: templateURLToMethods.keySet()) { + policyCatalog = templateURLToMethods.get(urlTemplate); + if (policyCatalog == null) continue;; + if (urlTemplate.match(urlStatic)) { + // need to delete duplicate from db. For example in db if url is api/books/1 now it becomes api/books/INTEGER so we need to delete api/books/1 + if (!urlTemplate.getTemplateString().equals(urlStatic.getUrl()) && shouldDeleteFromDb) { + // created new object because apiInfo id is altered below + deletedInfo.add(new ApiInfo.ApiInfoKey(apiInfo.getId().getApiCollectionId(), apiInfo.getId().getUrl(), apiInfo.getId().getMethod())); + } + // change api info url to template url + apiInfo.getId().setUrl(urlTemplate.getTemplateString()); + // merge with existing apiInfo if present + ApiInfo apiInfoFromMap = policyCatalog.getApiInfo(); + if (apiInfoFromMap != null) { + apiInfo.merge(apiInfoFromMap); + } + policyCatalog.setApiInfo(apiInfo); + return; + } + } + + if (shouldDeleteFromDb) { + deletedInfo.add(apiInfo.getId()); + } + + } + + public void main(List httpResponseParamsList, APICatalogSync apiCatalogSync, boolean fetchAllSTI) throws Exception { + if (httpResponseParamsList == null) httpResponseParamsList = new ArrayList<>(); + for (HttpResponseParams httpResponseParams: httpResponseParamsList) { + try { + process(httpResponseParams); + } catch (Exception e) { + logger.error(e.toString()); + ; + } + currentBatchSize += 1; + } + + if (apiCatalogSync != null) { + this.currentBatchSize = 0; + this.timeSinceLastSync = Context.now(); + syncWithDb(false, apiCatalogSync.dbState, fetchAllSTI); + } + } + + public static void main(String[] args) { + DaoInit.init(new ConnectionString("mongodb://172.18.0.2:27017/admini")); + Context.accountId.set(1_000_000); + RuntimeFilterDao.instance.initialiseFilters(); +// RuntimeFilterDao.instance.initialiseFilters(); +// List customFilterList = new ArrayList<>(); +// customFilterList.add(new ResponseCodeRuntimeFilter(200,299)); +// customFilterList.add(new FieldExistsFilter("labelId")); +// RuntimeFilter runtimeFilter = new RuntimeFilter(Context.now(),RuntimeFilter.UseCase.SET_CUSTOM_FIELD, "Check labelId", customFilterList, RuntimeFilter.Operator.AND, "check_label_id"); +// RuntimeFilterDao.instance.insertOne(runtimeFilter); + } + + public void process(HttpResponseParams httpResponseParams) throws Exception { + // logger.info("processing...."); + List customAuthTypes = SingleTypeInfo.activeCustomAuthTypes; + ApiInfo.ApiInfoKey apiInfoKey = ApiInfo.ApiInfoKey.generateFromHttpResponseParams(httpResponseParams); + PolicyCatalog policyCatalog = getApiInfoFromMap(apiInfoKey); + boolean addToReserve = false; + if (policyCatalog == null) { + addToReserve = true; + policyCatalog = new PolicyCatalog(new ApiInfo(apiInfoKey), new HashMap<>()); + } + ApiInfo apiInfo = policyCatalog.getApiInfo(); + Map filterSampleDataMap = policyCatalog.getFilterSampleDataMap(); + if (filterSampleDataMap == null) { + filterSampleDataMap = new HashMap<>(); + policyCatalog.setFilterSampleDataMap(filterSampleDataMap); + } + + int statusCode = httpResponseParams.getStatusCode(); + addToReserve = addToReserve && HttpResponseParams.validHttpResponseCode(statusCode); + + for (RuntimeFilter filter: filters) { + boolean filterResult = filter.process(httpResponseParams); + if (!filterResult) continue; + + RuntimeFilter.UseCase useCase = filter.getUseCase(); + boolean saveSample = false; + switch (useCase) { + case AUTH_TYPE: + saveSample = AuthPolicy.findAuthType(httpResponseParams, apiInfo, filter, customAuthTypes); + break; + case SET_CUSTOM_FIELD: + saveSample = SetFieldPolicy.setField(httpResponseParams, apiInfo, filter); + break; + case DETERMINE_API_ACCESS_TYPE: + saveSample = apiAccessTypePolicy.findApiAccessType(httpResponseParams, apiInfo, filter); + break; + default: + throw new Exception("Function for use case not defined"); + } + + // add sample data + if (saveSample) { + FilterSampleData filterSampleData = filterSampleDataMap.get(filter.getId()); + if (filterSampleData == null) { + filterSampleData = new FilterSampleData(apiInfo.getId(), filter.getId()); + filterSampleDataMap.put(filter.getId(), filterSampleData); + } + filterSampleData.getSamples().add(httpResponseParams.getOrig()); + } + } + + apiInfo.setLastSeen(Context.now()); + + if (addToReserve) { + ApiInfo reserveApiInfo = reserveApiInfoMap.get(apiInfo.getId()); + if (reserveApiInfo != null) apiInfo.merge(reserveApiInfo); + reserveApiInfoMap.put(apiInfo.getId(), apiInfo); + + for (Integer filterId: filterSampleDataMap.keySet()) { + FilterSampleData.FilterKey filterKey = new FilterSampleData.FilterKey(apiInfo.getId(), filterId); + FilterSampleData filterSampleData = filterSampleDataMap.get(filterId); + FilterSampleData reserveFilterSampleData = reserveFilterSampleDataMap.get(filterKey); + if (reserveFilterSampleData != null) { + filterSampleData.merge(reserveFilterSampleData); + } + + reserveFilterSampleDataMap.put(filterKey, filterSampleData); + } + } + + } + + public PolicyCatalog getApiInfoFromMap(ApiInfo.ApiInfoKey apiInfoKey) { + ApiInfoCatalog apiInfoCatalog = apiInfoCatalogMap.get(apiInfoKey.getApiCollectionId()); + if (apiInfoCatalog == null) { + ApiInfoCatalog apiInfoCatalog1 = new ApiInfoCatalog(new HashMap<>(), new HashMap<>(), new ArrayList<>()); + apiInfoCatalogMap.put(apiInfoKey.getApiCollectionId(), apiInfoCatalog1); + return null; + } + + Map strictURLToMethods = apiInfoCatalog.getStrictURLToMethods(); + if (strictURLToMethods == null) { + strictURLToMethods = new HashMap<>(); + apiInfoCatalog.setStrictURLToMethods(strictURLToMethods); + } + + Map templateURLToMethods = apiInfoCatalog.getTemplateURLToMethods(); + if (templateURLToMethods == null) { + templateURLToMethods = new HashMap<>(); + apiInfoCatalog.setTemplateURLToMethods(templateURLToMethods); + } + + URLStatic urlStatic = new URLStatic(apiInfoKey.getUrl(), apiInfoKey.getMethod()); + PolicyCatalog policyCatalog = strictURLToMethods.get(urlStatic); + if (policyCatalog != null) { + ApiInfo a = policyCatalog.getApiInfo(); + if (a == null) { + a = new ApiInfo(apiInfoKey); + policyCatalog.setApiInfo(a); + } + return policyCatalog; + } + + // there is a bug that /api/books was stored as api/books in db. So lastSeen has to be handled for this + String staticUrl = urlStatic.getUrl(); + staticUrl = APICatalogSync.trim(staticUrl); + URLStatic newUrlStatic = new URLStatic(staticUrl, urlStatic.getMethod()); + policyCatalog = strictURLToMethods.get(newUrlStatic); + if (policyCatalog != null) { + ApiInfo a = policyCatalog.getApiInfo(); + if (a == null) { + apiInfoKey.setUrl(newUrlStatic.getUrl()); + a = new ApiInfo(apiInfoKey); + policyCatalog.setApiInfo(a); + } + return policyCatalog; + } + + for (URLTemplate urlTemplate: templateURLToMethods.keySet()) { + policyCatalog = templateURLToMethods.get(urlTemplate); + if (policyCatalog == null) continue; + if (urlTemplate.match(urlStatic)) { + ApiInfo a = policyCatalog.getApiInfo(); + if (a == null) { + a = new ApiInfo(apiInfoKey.getApiCollectionId(), urlTemplate.getTemplateString(), apiInfoKey.getMethod()); + policyCatalog.setApiInfo(a); + } + return policyCatalog; + } + } + + return null; + } + + public static UpdateReturn getUpdates(Map apiInfoCatalogMap) { + List apiInfoList = new ArrayList<>(); + List filterSampleDataList = new ArrayList<>(); + List deletedApiInfoKeys = new ArrayList<>(); + for (ApiInfoCatalog apiInfoCatalog: apiInfoCatalogMap.values()) { + + Map strictURLToMethods = apiInfoCatalog.getStrictURLToMethods(); + if (strictURLToMethods == null) { + strictURLToMethods = new HashMap<>(); + apiInfoCatalog.setStrictURLToMethods(strictURLToMethods); + } + for (PolicyCatalog policyCatalog: strictURLToMethods.values()) { + ApiInfo apiInfo = policyCatalog.getApiInfo(); + if (apiInfo != null) { + apiInfoList.add(apiInfo); + } + Map filterSampleDataMap = policyCatalog.getFilterSampleDataMap(); + if (filterSampleDataMap != null) { + filterSampleDataList.addAll(filterSampleDataMap.values()); + } + } + + Map templateURLToMethods = apiInfoCatalog.getTemplateURLToMethods(); + if (templateURLToMethods == null) { + templateURLToMethods = new HashMap<>(); + apiInfoCatalog.setTemplateURLToMethods(templateURLToMethods); + } + for (PolicyCatalog policyCatalog: templateURLToMethods.values()) { + ApiInfo apiInfo = policyCatalog.getApiInfo(); + if (apiInfo != null) { + apiInfoList.add(apiInfo); + } + Map filterSampleDataMap = policyCatalog.getFilterSampleDataMap(); + if (filterSampleDataMap != null) { + filterSampleDataList.addAll(filterSampleDataMap.values()); + } + } + + deletedApiInfoKeys.addAll(apiInfoCatalog.getDeletedInfo()); + } + + List> updatesForApiInfo = getUpdatesForApiInfo(apiInfoList, deletedApiInfoKeys); + List> updatesForSampleData = getUpdatesForSampleData(filterSampleDataList,deletedApiInfoKeys); + + return new UpdateReturn(updatesForApiInfo, updatesForSampleData); + } + + public static class UpdateReturn { + public List> updatesForApiInfo; + public List> updatesForSampleData; + + public UpdateReturn(List> updatesForApiInfo, List> updatesForSampleData) { + this.updatesForApiInfo = updatesForApiInfo; + this.updatesForSampleData = updatesForSampleData; + } + } + + public static List> getUpdatesForApiInfo(List apiInfoList, List deletedApiInfoList) { + + List> updates = new ArrayList<>(); + for (ApiInfo apiInfo: apiInfoList) { + + List subUpdates = new ArrayList<>(); + + // allAuthTypesFound + Set> allAuthTypesFound = apiInfo.getAllAuthTypesFound(); + if (allAuthTypesFound.isEmpty()) { + // to make sure no field is null (so setting empty objects) + subUpdates.add(Updates.setOnInsert(ApiInfo.ALL_AUTH_TYPES_FOUND, new HashSet<>())); + } else { + subUpdates.add(Updates.addEachToSet(ApiInfo.ALL_AUTH_TYPES_FOUND, Arrays.asList(allAuthTypesFound.toArray()))); + } + + // apiAccessType + Set apiAccessTypes = apiInfo.getApiAccessTypes(); + if (apiAccessTypes.isEmpty()) { + // to make sure no field is null (so setting empty objects) + subUpdates.add(Updates.setOnInsert(ApiInfo.API_ACCESS_TYPES, new HashSet<>())); + } else { + subUpdates.add(Updates.addEachToSet(ApiInfo.API_ACCESS_TYPES, Arrays.asList(apiAccessTypes.toArray()))); + } + + // violations + Map violationsMap = apiInfo.getViolations(); + if (violationsMap == null || violationsMap.isEmpty()) { + // to make sure no field is null (so setting empty objects) + subUpdates.add(Updates.setOnInsert(ApiInfo.VIOLATIONS, new HashMap<>())); + } else { + for (String customKey: violationsMap.keySet()) { + subUpdates.add(Updates.set(ApiInfo.VIOLATIONS + "." + customKey, violationsMap.get(customKey))); + } + } + + // last seen + subUpdates.add(Updates.set(ApiInfo.LAST_SEEN, apiInfo.getLastSeen())); + + updates.add( + new UpdateOneModel<>( + ApiInfoDao.getFilter(apiInfo.getId()), + Updates.combine(subUpdates), + new UpdateOptions().upsert(true) + ) + ); + + } + + if (deletedApiInfoList == null) deletedApiInfoList = new ArrayList<>(); + for (ApiInfo.ApiInfoKey apiInfoKey: deletedApiInfoList) { + updates.add( + new DeleteOneModel<>( + ApiInfoDao.getFilter(apiInfoKey) + ) + ); + } + + + return updates; + } + + public static List> getUpdatesForSampleData(List filterSampleDataList, List apiInfoRemoveList) { + ArrayList> bulkUpdates = new ArrayList<>(); + if (filterSampleDataList == null) filterSampleDataList = new ArrayList<>(); + if (apiInfoRemoveList == null) apiInfoRemoveList= new ArrayList<>(); + + for (FilterSampleData filterSampleData: filterSampleDataList) { + List sampleData = filterSampleData.getSamples().get(); + Bson bson = Updates.pushEach(FilterSampleData.SAMPLES+".elements", sampleData, new PushOptions().slice(-1 * FilterSampleData.cap)); + bulkUpdates.add( + new UpdateOneModel<>( + FilterSampleDataDao.getFilter(filterSampleData.getId().getApiInfoKey(), filterSampleData.getId().getFilterId()), + bson, + new UpdateOptions().upsert(true) + ) + ); + } + + for (ApiInfo.ApiInfoKey apiInfoKey: apiInfoRemoveList) { + bulkUpdates.add( + new DeleteManyModel<>(FilterSampleDataDao.getFilterForApiInfoKey(apiInfoKey)) + ); + } + + return bulkUpdates; + } + + public List getFilters() { + return filters; + } + + public void setFilters(List filters) { + this.filters = filters; + } + + public boolean isProcessCalledAtLeastOnce() { + return processCalledAtLeastOnce; + } + + public void setProcessCalledAtLeastOnce(boolean processCalledAtLeastOnce) { + this.processCalledAtLeastOnce = processCalledAtLeastOnce; + } + + public ApiAccessTypePolicy getApiAccessTypePolicy() { + return apiAccessTypePolicy; + } + + public void setApiAccessTypePolicy(ApiAccessTypePolicy apiAccessTypePolicy) { + this.apiAccessTypePolicy = apiAccessTypePolicy; + } + + + public Map getReserveApiInfoMap() { + return reserveApiInfoMap; + } + + public void setReserveApiInfoMap(Map reserveApiInfoMap) { + this.reserveApiInfoMap = reserveApiInfoMap; + } + + public Map getReserveFilterSampleDataMap() { + return reserveFilterSampleDataMap; + } + + public void setReserveFilterSampleDataMap(Map reserveFilterSampleDataMap) { + this.reserveFilterSampleDataMap = reserveFilterSampleDataMap; + } + + public Map getApiInfoCatalogMap() { + return apiInfoCatalogMap; + } + + public void setApiInfoCatalogMap(Map apiInfoCatalogMap) { + this.apiInfoCatalogMap = apiInfoCatalogMap; + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/policies/AktoPolicyNew.java b/apps/api-runtime/src/main/java/com/akto/runtime/policies/AktoPolicyNew.java new file mode 100644 index 0000000000..877c119887 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/policies/AktoPolicyNew.java @@ -0,0 +1,381 @@ +package com.akto.runtime.policies; + +import com.akto.dao.*; +import com.akto.dao.context.Context; +import com.akto.dto.*; +import com.akto.dto.runtime_filters.RuntimeFilter; +import com.akto.dto.type.APICatalog; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLStatic; +import com.akto.dto.type.URLTemplate; +import com.akto.runtime.APICatalogSync; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.*; +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +import java.util.*; + +import static com.akto.runtime.APICatalogSync.createUrlTemplate; + +public class AktoPolicyNew { + + private List filters = new ArrayList<>(); + private Map apiInfoCatalogMap = new HashMap<>(); + boolean processCalledAtLeastOnce = false; + ApiAccessTypePolicy apiAccessTypePolicy = new ApiAccessTypePolicy(null); + boolean redact = false; + + private static final Logger logger = LoggerFactory.getLogger(AktoPolicyNew.class); + + public void fetchFilters() { + this.filters = RuntimeFilterDao.instance.findAll(new BasicDBObject()); + } + + public AktoPolicyNew(boolean fetchAllSTI) { + buildFromDb(fetchAllSTI); + } + + public void buildFromDb(boolean fetchAllSTI) { + fetchFilters(); + + AccountSettings accountSettings = AccountSettingsDao.instance.findOne(new BasicDBObject()); + if (accountSettings != null) { + List cidrList = accountSettings.getPrivateCidrList(); + if ( cidrList != null && !cidrList.isEmpty()) { + apiAccessTypePolicy.setPrivateCidrList(cidrList); + } + redact = accountSettings.isRedactPayload(); + } + + apiInfoCatalogMap = new HashMap<>(); + + List apiInfoList; + if (fetchAllSTI) { + apiInfoList = ApiInfoDao.instance.findAll(new BasicDBObject()); + } else { + List apiCollectionIds = ApiCollectionsDao.instance.fetchNonTrafficApiCollectionsIds(); + apiInfoList = ApiInfoDao.instance.findAll(Filters.in("_id.apiCollectionId", apiCollectionIds)); + } + + List filterSampleDataList = FilterSampleDataDao.instance.findAll(new BasicDBObject()); + + Map> filterSampleDataMapToApiInfo = new HashMap<>(); + for (FilterSampleData filterSampleData: filterSampleDataList) { + FilterSampleData.FilterKey filterKey = filterSampleData.getId(); + ApiInfo.ApiInfoKey apiInfoKey = filterKey.getApiInfoKey(); + + Map filterSampleDataMap = filterSampleDataMapToApiInfo.getOrDefault(apiInfoKey, new HashMap<>()); + filterSampleDataMap.put(filterKey.filterId, filterSampleData); + filterSampleDataMapToApiInfo.put(apiInfoKey, filterSampleDataMap); + } + + for (ApiInfo apiInfo: apiInfoList) { + try { + Map filterSampleDataMap = filterSampleDataMapToApiInfo.get(apiInfo.getId()); + fillApiInfoInCatalog(apiInfo, filterSampleDataMap); + } catch (Exception e) { + logger.error(e.getMessage() + " " + e.getCause()); + } + } + + } + + public void syncWithDb(boolean initialising, boolean fetchAllSTI) { + // logger.info("Syncing with db"); + if (!initialising) { + AktoPolicy.UpdateReturn updateReturn = AktoPolicy.getUpdates(apiInfoCatalogMap); + List> writesForApiInfo = updateReturn.updatesForApiInfo; + List> writesForSampleData = updateReturn.updatesForSampleData; + logger.info("Writing to db: " + "writesForApiInfoSize="+writesForApiInfo.size() + " writesForSampleData="+ writesForSampleData.size()); + try { + if (writesForApiInfo.size() > 0) ApiInfoDao.instance.getMCollection().bulkWrite(writesForApiInfo); + if (!redact && writesForSampleData.size() > 0) FilterSampleDataDao.instance.getMCollection().bulkWrite(writesForSampleData); + } catch (Exception e) { + logger.error(e.toString()); + } + } + + buildFromDb(fetchAllSTI); + } + + public void fillApiInfoInCatalog(ApiInfo apiInfo, Map filterSampleDataMap) { + ApiInfo.ApiInfoKey apiInfoKey = apiInfo.getId(); + ApiInfoCatalog apiInfoCatalog = apiInfoCatalogMap.get(apiInfoKey.getApiCollectionId()); + if (apiInfoCatalog == null) { + apiInfoCatalog = new ApiInfoCatalog(new HashMap<>(), new HashMap<>(), new ArrayList<>()); + apiInfoCatalogMap.put(apiInfoKey.getApiCollectionId(), apiInfoCatalog); + } + + PolicyCatalog policyCatalog = new PolicyCatalog(apiInfo, filterSampleDataMap); + + if (APICatalog.isTemplateUrl(apiInfoKey.url)) { + URLTemplate urlTemplate = createUrlTemplate(apiInfoKey.url, apiInfoKey.method); + Map templateURLToMethods = apiInfoCatalog.getTemplateURLToMethods(); + templateURLToMethods.putIfAbsent(urlTemplate, policyCatalog); + } else { + URLStatic urlStatic = new URLStatic(apiInfoKey.getUrl(), apiInfoKey.getMethod()); + Map strictURLToMethods = apiInfoCatalog.getStrictURLToMethods(); + strictURLToMethods.putIfAbsent(urlStatic, policyCatalog); + } + + } + + public void main(List httpResponseParamsList, boolean syncNow, boolean fetchAllSTI) throws Exception { + if (httpResponseParamsList == null) httpResponseParamsList = new ArrayList<>(); + for (HttpResponseParams httpResponseParams: httpResponseParamsList) { + try { + process(httpResponseParams); + } catch (Exception e) { + logger.error(e.toString()); + ; + } + } + + if (syncNow) { + syncWithDb(false, fetchAllSTI); + } + } + + public void process(HttpResponseParams httpResponseParams) throws Exception { + List customAuthTypes = SingleTypeInfo.activeCustomAuthTypes; + ApiInfo.ApiInfoKey apiInfoKey = ApiInfo.ApiInfoKey.generateFromHttpResponseParams(httpResponseParams); + PolicyCatalog policyCatalog = getApiInfoFromMap(apiInfoKey); + ApiInfo apiInfo = policyCatalog.getApiInfo(); + + Map filterSampleDataMap = policyCatalog.getFilterSampleDataMap(); + if (filterSampleDataMap == null) { + filterSampleDataMap = new HashMap<>(); + policyCatalog.setFilterSampleDataMap(filterSampleDataMap); + } + + int statusCode = httpResponseParams.getStatusCode(); + if (!HttpResponseParams.validHttpResponseCode(statusCode)) return; + + for (RuntimeFilter filter: filters) { + boolean filterResult = filter.process(httpResponseParams); + if (!filterResult) continue; + + RuntimeFilter.UseCase useCase = filter.getUseCase(); + boolean saveSample = false; + switch (useCase) { + case AUTH_TYPE: + saveSample = AuthPolicy.findAuthType(httpResponseParams, apiInfo, filter, customAuthTypes); + break; + case SET_CUSTOM_FIELD: + saveSample = SetFieldPolicy.setField(httpResponseParams, apiInfo, filter); + break; + case DETERMINE_API_ACCESS_TYPE: + saveSample = apiAccessTypePolicy.findApiAccessType(httpResponseParams, apiInfo, filter); + break; + default: + throw new Exception("Function for use case not defined"); + } + + // add sample data + if (saveSample) { + FilterSampleData filterSampleData = filterSampleDataMap.get(filter.getId()); + if (filterSampleData == null) { + filterSampleData = new FilterSampleData(apiInfo.getId(), filter.getId()); + filterSampleDataMap.put(filter.getId(), filterSampleData); + } + filterSampleData.getSamples().add(httpResponseParams.getOrig()); + } + } + + apiInfo.setLastSeen(Context.now()); + + } + + public PolicyCatalog getApiInfoFromMap(ApiInfo.ApiInfoKey apiInfoKey) { + ApiInfoCatalog apiInfoCatalog = apiInfoCatalogMap.get(apiInfoKey.getApiCollectionId()); + if (apiInfoCatalog == null) { + apiInfoCatalog = new ApiInfoCatalog(new HashMap<>(), new HashMap<>(), new ArrayList<>()); + apiInfoCatalogMap.put(apiInfoKey.getApiCollectionId(), apiInfoCatalog); + } + + Map strictURLToMethods = apiInfoCatalog.getStrictURLToMethods(); + if (strictURLToMethods == null) { + strictURLToMethods = new HashMap<>(); + apiInfoCatalog.setStrictURLToMethods(strictURLToMethods); + } + + Map templateURLToMethods = apiInfoCatalog.getTemplateURLToMethods(); + if (templateURLToMethods == null) { + templateURLToMethods = new HashMap<>(); + apiInfoCatalog.setTemplateURLToMethods(templateURLToMethods); + } + + URLStatic urlStatic = new URLStatic(apiInfoKey.getUrl(), apiInfoKey.getMethod()); + PolicyCatalog policyCatalog = strictURLToMethods.get(urlStatic); + if (policyCatalog != null) { + return policyCatalog; + } + + for (URLTemplate urlTemplate: templateURLToMethods.keySet()) { + policyCatalog = templateURLToMethods.get(urlTemplate); + if (policyCatalog == null) continue; + if (urlTemplate.match(urlStatic)) { + ApiInfo a = policyCatalog.getApiInfo(); + if (a == null) { + a = new ApiInfo(apiInfoKey.getApiCollectionId(), urlTemplate.getTemplateString(), apiInfoKey.getMethod()); + policyCatalog.setApiInfo(a); + } + return policyCatalog; + } + } + + PolicyCatalog newPolicyCatalog = new PolicyCatalog(new ApiInfo(apiInfoKey), new HashMap<>()); + strictURLToMethods.put(urlStatic, newPolicyCatalog); + + return newPolicyCatalog; + } + + public static AktoPolicy.UpdateReturn getUpdates(Map apiInfoCatalogMap) { + List apiInfoList = new ArrayList<>(); + List filterSampleDataList = new ArrayList<>(); + for (ApiInfoCatalog apiInfoCatalog: apiInfoCatalogMap.values()) { + + Map strictURLToMethods = apiInfoCatalog.getStrictURLToMethods(); + Map templateURLToMethods = apiInfoCatalog.getTemplateURLToMethods(); + + List policyCatalogList = new ArrayList<>(); + policyCatalogList.addAll(strictURLToMethods.values()); + policyCatalogList.addAll(templateURLToMethods.values()); + + for (PolicyCatalog policyCatalog: policyCatalogList) { + ApiInfo apiInfo = policyCatalog.getApiInfo(); + if (apiInfo != null) { + apiInfoList.add(apiInfo); + } + Map filterSampleDataMap = policyCatalog.getFilterSampleDataMap(); + if (filterSampleDataMap != null) { + filterSampleDataList.addAll(filterSampleDataMap.values()); + } + } + } + + List> updatesForApiInfo = getUpdatesForApiInfo(apiInfoList); + List> updatesForSampleData = getUpdatesForSampleData(filterSampleDataList); + + return new AktoPolicy.UpdateReturn(updatesForApiInfo, updatesForSampleData); + } + + public static class UpdateReturn { + public List> updatesForApiInfo; + public List> updatesForSampleData; + + public UpdateReturn(List> updatesForApiInfo, List> updatesForSampleData) { + this.updatesForApiInfo = updatesForApiInfo; + this.updatesForSampleData = updatesForSampleData; + } + } + + public static List> getUpdatesForApiInfo(List apiInfoList) { + + List> updates = new ArrayList<>(); + for (ApiInfo apiInfo: apiInfoList) { + + List subUpdates = new ArrayList<>(); + + // allAuthTypesFound + Set> allAuthTypesFound = apiInfo.getAllAuthTypesFound(); + if (allAuthTypesFound.isEmpty()) { + // to make sure no field is null (so setting empty objects) + subUpdates.add(Updates.setOnInsert(ApiInfo.ALL_AUTH_TYPES_FOUND, new HashSet<>())); + } else { + subUpdates.add(Updates.addEachToSet(ApiInfo.ALL_AUTH_TYPES_FOUND, Arrays.asList(allAuthTypesFound.toArray()))); + } + + // apiAccessType + Set apiAccessTypes = apiInfo.getApiAccessTypes(); + if (apiAccessTypes.isEmpty()) { + // to make sure no field is null (so setting empty objects) + subUpdates.add(Updates.setOnInsert(ApiInfo.API_ACCESS_TYPES, new HashSet<>())); + } else { + subUpdates.add(Updates.addEachToSet(ApiInfo.API_ACCESS_TYPES, Arrays.asList(apiAccessTypes.toArray()))); + } + + // violations + Map violationsMap = apiInfo.getViolations(); + if (violationsMap == null || violationsMap.isEmpty()) { + // to make sure no field is null (so setting empty objects) + subUpdates.add(Updates.setOnInsert(ApiInfo.VIOLATIONS, new HashMap<>())); + } else { + for (String customKey: violationsMap.keySet()) { + subUpdates.add(Updates.set(ApiInfo.VIOLATIONS + "." + customKey, violationsMap.get(customKey))); + } + } + + // last seen + subUpdates.add(Updates.set(ApiInfo.LAST_SEEN, apiInfo.getLastSeen())); + + updates.add( + new UpdateOneModel<>( + ApiInfoDao.getFilter(apiInfo.getId()), + Updates.combine(subUpdates), + new UpdateOptions().upsert(true) + ) + ); + + } + + return updates; + } + + public static List> getUpdatesForSampleData(List filterSampleDataList) { + ArrayList> bulkUpdates = new ArrayList<>(); + if (filterSampleDataList == null) filterSampleDataList = new ArrayList<>(); + + for (FilterSampleData filterSampleData: filterSampleDataList) { + List sampleData = filterSampleData.getSamples().get(); + Bson bson = Updates.pushEach(FilterSampleData.SAMPLES+".elements", sampleData, new PushOptions().slice(-1 * FilterSampleData.cap)); + bulkUpdates.add( + new UpdateOneModel<>( + FilterSampleDataDao.getFilter(filterSampleData.getId().getApiInfoKey(), filterSampleData.getId().getFilterId()), + bson, + new UpdateOptions().upsert(true) + ) + ); + } + + return bulkUpdates; + } + + public List getFilters() { + return filters; + } + + public void setFilters(List filters) { + this.filters = filters; + } + + public boolean isProcessCalledAtLeastOnce() { + return processCalledAtLeastOnce; + } + + public void setProcessCalledAtLeastOnce(boolean processCalledAtLeastOnce) { + this.processCalledAtLeastOnce = processCalledAtLeastOnce; + } + + public ApiAccessTypePolicy getApiAccessTypePolicy() { + return apiAccessTypePolicy; + } + + public void setApiAccessTypePolicy(ApiAccessTypePolicy apiAccessTypePolicy) { + this.apiAccessTypePolicy = apiAccessTypePolicy; + } + + + public Map getApiInfoCatalogMap() { + return apiInfoCatalogMap; + } + + public void setApiInfoCatalogMap(Map apiInfoCatalogMap) { + this.apiInfoCatalogMap = apiInfoCatalogMap; + } +} + + diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/policies/ApiAccessTypePolicy.java b/apps/api-runtime/src/main/java/com/akto/runtime/policies/ApiAccessTypePolicy.java new file mode 100644 index 0000000000..e68245c140 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/policies/ApiAccessTypePolicy.java @@ -0,0 +1,71 @@ +package com.akto.runtime.policies; + +import com.akto.dto.ApiInfo; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.runtime_filters.RuntimeFilter; +import com.akto.parsers.HttpCallParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.web.util.matcher.IpAddressMatcher; + +import java.util.ArrayList; +import java.util.List; + +public class ApiAccessTypePolicy { + private List privateCidrList; + public static final String X_FORWARDED_FOR = "x-forwarded-for"; + private static final Logger logger = LoggerFactory.getLogger(ApiAccessTypePolicy.class); + + public ApiAccessTypePolicy(List privateCidrList) { + this.privateCidrList = privateCidrList; + } + + + public boolean findApiAccessType(HttpResponseParams httpResponseParams, ApiInfo apiInfo, RuntimeFilter filter) { + if (privateCidrList == null || privateCidrList.isEmpty()) return false; + List ipList = httpResponseParams.getRequestParams().getHeaders().get(X_FORWARDED_FOR); + + if (ipList == null) { + ipList = new ArrayList<>(); + } + + String sourceIP = httpResponseParams.getSourceIP(); + + if (sourceIP != null && !sourceIP.isEmpty()) { + ipList.add(sourceIP); + } + + for (String ip: ipList) { + if (ip == null) continue; + ip = ip.replaceAll(" ", ""); + try { + boolean result = ipInCidr(ip); + if (!result) { + apiInfo.getApiAccessTypes().add(ApiInfo.ApiAccessType.PUBLIC); + return false; + } + } catch (Exception e) { + return false; + } + } + + apiInfo.getApiAccessTypes().add(ApiInfo.ApiAccessType.PRIVATE); + + return false; + } + + public boolean ipInCidr(String ip) { + IpAddressMatcher ipAddressMatcher; + for (String cidr: privateCidrList) { + ipAddressMatcher = new IpAddressMatcher(cidr); + boolean result = ipAddressMatcher.matches(ip); + if (result) return true; + } + + return false; + } + + public void setPrivateCidrList(List privateCidrList) { + this.privateCidrList = privateCidrList; + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/policies/AuthPolicy.java b/apps/api-runtime/src/main/java/com/akto/runtime/policies/AuthPolicy.java new file mode 100644 index 0000000000..278d01e65d --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/policies/AuthPolicy.java @@ -0,0 +1,141 @@ +package com.akto.runtime.policies; + +import com.akto.dto.ApiInfo; +import com.akto.dto.CustomAuthType; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.runtime_filters.RuntimeFilter; +import com.akto.dto.type.KeyTypes; +import com.akto.util.JSONUtils; + +import com.mongodb.BasicDBObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +public class AuthPolicy { + + public static final String AUTHORIZATION_HEADER_NAME = "authorization"; + public static final String COOKIE_NAME = "cookie"; + private static final Logger logger = LoggerFactory.getLogger(AuthPolicy.class); + + private static List findBearerBasicAuth(String header, String value){ + value = value.trim(); + boolean twoFields = value.split(" ").length == 2; + if (twoFields && value.substring(0, Math.min(6, value.length())).equalsIgnoreCase("bearer")) { + return Collections.singletonList(ApiInfo.AuthType.BEARER); + } else if (twoFields && value.substring(0, Math.min(5, value.length())).equalsIgnoreCase("basic")) { + return Collections.singletonList(ApiInfo.AuthType.BASIC); + } else if (header.equals(AUTHORIZATION_HEADER_NAME) || header.equals("auth")) { + return Collections.singletonList(ApiInfo.AuthType.AUTHORIZATION_HEADER); + } + return new ArrayList<>(); + } + + public static Map parseCookie(List cookieList){ + Map cookieMap = new HashMap<>(); + if(cookieList==null)return cookieMap; + for (String cookieValues : cookieList) { + String[] cookies = cookieValues.split(";"); + for (String cookie : cookies) { + cookie=cookie.trim(); + String[] cookieFields = cookie.split("="); + boolean twoCookieFields = cookieFields.length == 2; + if (twoCookieFields) { + if(!cookieMap.containsKey(cookieFields[0])){ + cookieMap.put(cookieFields[0], cookieFields[1]); + } + } + } + } + return cookieMap; + } + + public static boolean findAuthType(HttpResponseParams httpResponseParams, ApiInfo apiInfo, RuntimeFilter filter, List customAuthTypes) { + Set> allAuthTypesFound = apiInfo.getAllAuthTypesFound(); + if (allAuthTypesFound == null) allAuthTypesFound = new HashSet<>(); + + // NOTE: custom api-token can be in multiple headers. For example twitter api sends 2 headers access-key and access-token + + + // find Authorization header + Map> headers = httpResponseParams.getRequestParams().getHeaders(); + List cookieList = headers.getOrDefault(COOKIE_NAME, new ArrayList<>()); + Map cookieMap = parseCookie(cookieList); + Set authTypes = new HashSet<>(); + + BasicDBObject flattenedPayload = null; + try{ + BasicDBObject basicDBObject = BasicDBObject.parse(httpResponseParams.getRequestParams().getPayload()); + flattenedPayload = JSONUtils.flattenWithDots(basicDBObject); + } catch (Exception e){ + } + for (CustomAuthType customAuthType : customAuthTypes) { + + Set headerAndCookieKeys = new HashSet<>(); + headerAndCookieKeys.addAll(headers.keySet()); + headerAndCookieKeys.addAll(cookieMap.keySet()); + + // Find custom auth type in header and cookie + List customAuthTypeHeaderKeys = customAuthType.getHeaderKeys(); + if (!headerAndCookieKeys.isEmpty() && !customAuthTypeHeaderKeys.isEmpty() && headerAndCookieKeys.containsAll(customAuthTypeHeaderKeys)) { + authTypes.add(ApiInfo.AuthType.CUSTOM); + break; + } + + // Find custom auth type in payload + List customAuthTypePayloadKeys = customAuthType.getPayloadKeys(); + if(flattenedPayload != null && !flattenedPayload.isEmpty() && !customAuthTypePayloadKeys.isEmpty() && flattenedPayload.keySet().containsAll(customAuthTypePayloadKeys)){ + authTypes.add(ApiInfo.AuthType.CUSTOM); + break; + } + } + + // find bearer or basic tokens in any header + for (String header : headers.keySet()) { + List headerValues = headers.getOrDefault(header, new ArrayList<>()); + for (String value : headerValues) { + authTypes.addAll(findBearerBasicAuth(header, value)); + } + } + + boolean flag = false; + for (String cookieKey : cookieMap.keySet()) { + // Find bearer or basic token in cookie values + authTypes.addAll(findBearerBasicAuth(cookieKey, cookieMap.get(cookieKey))); + // Find JWT in cookie values + if (KeyTypes.isJWT(cookieMap.get(cookieKey))) { + authTypes.add(ApiInfo.AuthType.JWT); + flag = true; + } + } + + // Find JWT in header values + for (String headerName: headers.keySet()) { + if (flag) break; + if (headerName.equals(AUTHORIZATION_HEADER_NAME)){ + continue; + } + List headerValues =headers.getOrDefault(headerName,new ArrayList<>()); + for (String header: headerValues) { + if (KeyTypes.isJWT(header)){ + authTypes.add(ApiInfo.AuthType.JWT); + flag = true; + break; + } + } + } + + boolean returnValue = false; + if (authTypes.isEmpty()) { + authTypes.add(ApiInfo.AuthType.UNAUTHENTICATED); + returnValue = true; + } + + + allAuthTypesFound.add(authTypes); + apiInfo.setAllAuthTypesFound(allAuthTypesFound); + + return returnValue; + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/runtime/policies/SetFieldPolicy.java b/apps/api-runtime/src/main/java/com/akto/runtime/policies/SetFieldPolicy.java new file mode 100644 index 0000000000..7bb070df3c --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/runtime/policies/SetFieldPolicy.java @@ -0,0 +1,20 @@ +package com.akto.runtime.policies; + +import com.akto.dao.context.Context; +import com.akto.dto.ApiInfo; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.runtime_filters.RuntimeFilter; + +import java.util.HashMap; +import java.util.Map; + +public class SetFieldPolicy { + public static boolean setField(HttpResponseParams httpResponseParams, ApiInfo apiInfo, RuntimeFilter filter) { + Map result = apiInfo.getViolations(); + if (result == null) result = new HashMap<>(); + String fieldName = filter.getCustomFieldName(); + result.put(fieldName, Context.now()); + apiInfo.setViolations(result); + return true; + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/utils/CustomAuthUtil.java b/apps/api-runtime/src/main/java/com/akto/utils/CustomAuthUtil.java new file mode 100644 index 0000000000..7d6ffa031f --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/utils/CustomAuthUtil.java @@ -0,0 +1,89 @@ +package com.akto.utils; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.ArrayList; + +import com.mongodb.client.model.*; +import org.bson.conversions.Bson; + +import com.akto.dao.ApiInfoDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.CustomAuthType; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.runtime.policies.AuthPolicy; + +public class CustomAuthUtil { + + public static Bson getFilters(ApiInfo apiInfo,Boolean isHeader,List params){ + return Filters.and( + Filters.eq(SingleTypeInfo._RESPONSE_CODE, -1), + Filters.eq(SingleTypeInfo._URL,apiInfo.getId().getUrl()), + Filters.eq(SingleTypeInfo._API_COLLECTION_ID,apiInfo.getId().getApiCollectionId()), + Filters.eq(SingleTypeInfo._METHOD,apiInfo.getId().getMethod().name()), + Filters.eq(SingleTypeInfo._IS_HEADER,isHeader), + Filters.in(SingleTypeInfo._PARAM,params) + ); + } + public static void customAuthTypeUtil(List customAuthTypes){ + + Set unauthenticatedTypes = new HashSet<>(Collections.singletonList(ApiInfo.AuthType.UNAUTHENTICATED)); + List apiInfos = ApiInfoDao.instance.findAll(Filters.eq("allAuthTypesFound",unauthenticatedTypes)); + + Set customTypes = new HashSet<>(Collections.singletonList(ApiInfo.AuthType.CUSTOM)); + Set> authTypes = new HashSet<>(Collections.singletonList(customTypes)); + + List COOKIE_LIST = Collections.singletonList("cookie"); + + List> apiInfosUpdates = new ArrayList<>(); + + for (ApiInfo apiInfo : apiInfos) { + for (CustomAuthType customAuthType : customAuthTypes) { + + Set headerAndCookieKeys = new HashSet<>(); + List headerSTIs = SingleTypeInfoDao.instance.findAll(getFilters(apiInfo, true, customAuthType.getHeaderKeys())); + if(headerSTIs!=null){ + for(SingleTypeInfo sti:headerSTIs){ + headerAndCookieKeys.add(sti.getParam()); + } + } + SingleTypeInfo cookieSTI = SingleTypeInfoDao.instance.findOne(getFilters(apiInfo, true, COOKIE_LIST)); + if(cookieSTI!=null){ + Map cookieMap = AuthPolicy.parseCookie(new ArrayList<>(cookieSTI.getValues().getElements())); + headerAndCookieKeys.addAll(cookieMap.keySet()); + } + + // checking headerAuthKeys in header and cookie in any unathenticated API + if (!headerAndCookieKeys.isEmpty() && !customAuthType.getHeaderKeys().isEmpty() && headerAndCookieKeys.containsAll(customAuthType.getHeaderKeys())) { + UpdateOneModel update = new UpdateOneModel<>( + ApiInfoDao.getFilter(apiInfo.getId()), + Updates.set(ApiInfo.ALL_AUTH_TYPES_FOUND, authTypes), + new UpdateOptions().upsert(false) + ); + apiInfosUpdates.add(update); + break; + } + + // checking if all payload keys occur in any unauthenticated API + List payloadSTIs = SingleTypeInfoDao.instance.findAll(getFilters(apiInfo, false, customAuthType.getPayloadKeys())); + if (payloadSTIs!=null && payloadSTIs.size()==customAuthType.getPayloadKeys().size()) { + + UpdateOneModel update = new UpdateOneModel<>( + ApiInfoDao.getFilter(apiInfo.getId()), + Updates.set(ApiInfo.ALL_AUTH_TYPES_FOUND, authTypes), + new UpdateOptions().upsert(false) + ); + apiInfosUpdates.add(update); + } + } + + if (apiInfosUpdates.size() > 0) { + ApiInfoDao.instance.getMCollection().bulkWrite(apiInfosUpdates); + } + } + } +} diff --git a/apps/api-runtime/src/main/java/com/akto/utils/RedactSampleData.java b/apps/api-runtime/src/main/java/com/akto/utils/RedactSampleData.java new file mode 100644 index 0000000000..f756d48ff0 --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/utils/RedactSampleData.java @@ -0,0 +1,183 @@ +package com.akto.utils; + +import com.akto.dto.HttpRequestParams; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.parsers.HttpCallParser; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.mongodb.BasicDBObject; + +import java.io.IOException; +import java.util.*; + +public class RedactSampleData { + static ObjectMapper mapper = new ObjectMapper(); + static JsonFactory factory = mapper.getFactory(); + + public static final String redactValue = "****"; + + public static String redact(String sample) throws Exception { + HttpResponseParams httpResponseParams = HttpCallParser.parseKafkaMessage(sample); + return redact(httpResponseParams); + } + + // never use this function directly. This alters the httpResponseParams + public static String redact(HttpResponseParams httpResponseParams) throws Exception { + // response headers + Map> responseHeaders = httpResponseParams.getHeaders(); + if (responseHeaders == null) responseHeaders = new HashMap<>(); + responseHeaders.replaceAll((n, v) -> Collections.singletonList(redactValue)); + + // response payload + String responsePayload = httpResponseParams.getPayload(); + if (responsePayload == null) responsePayload = "{}"; + try { + JsonParser jp = factory.createParser(responsePayload); + JsonNode node = mapper.readTree(jp); + change(node, redactValue); + if (node != null) { + responsePayload = node.toString(); + } else { + responsePayload = "{}"; + } + } catch (Exception e) { + responsePayload = "{}"; + } + + httpResponseParams.setPayload(responsePayload); + + // request headers + Map> requestHeaders = httpResponseParams.requestParams.getHeaders(); + if (requestHeaders == null) requestHeaders = new HashMap<>(); + requestHeaders.replaceAll((n, v) -> Collections.singletonList(redactValue)); + + // request payload + String requestPayload = httpResponseParams.requestParams.getPayload(); + if (requestPayload == null) requestPayload = "{}"; + try { + JsonParser jp = factory.createParser(requestPayload); + JsonNode node = mapper.readTree(jp); + change(node, redactValue); + if (node != null) { + requestPayload= node.toString(); + } else { + requestPayload = "{}"; + } + } catch (Exception e) { + requestPayload = "{}"; + } + + httpResponseParams.requestParams.setPayload(requestPayload); + + // ip + httpResponseParams.setSourceIP(redactValue); + + return convertHttpRespToOriginalString(httpResponseParams); + + } + + public static void change(JsonNode parent, String newValue) { + if (parent == null) return; + + if (parent.isArray()) { + ArrayNode arrayNode = (ArrayNode) parent; + for(int i = 0; i < arrayNode.size(); i++) { + JsonNode arrayElement = arrayNode.get(i); + if (arrayElement.isValueNode()) { + arrayNode.set(i, new TextNode(newValue)); + } else { + change(arrayElement, newValue); + } + } + } else { + Iterator fieldNames = parent.fieldNames(); + while(fieldNames.hasNext()) { + String f = fieldNames.next(); + JsonNode fieldValue = parent.get(f); + if (fieldValue.isValueNode()) { + ((ObjectNode) parent).put(f, newValue); + } else { + change(fieldValue, newValue); + } + } + } + + } + + public static String convertOriginalReqRespToString(OriginalHttpRequest request, OriginalHttpResponse response) { + BasicDBObject req = new BasicDBObject(); + if (request != null) { + req.put("url", request.getUrl()); + req.put("method", request.getMethod()); + req.put("type", request.getType()); + req.put("queryParams", request.getQueryParams()); + req.put("body", request.getBody()); + req.put("headers", convertHeaders(request.getHeaders())); + } + + BasicDBObject resp = new BasicDBObject(); + if (response != null) { + resp.put("statusCode", response.getStatusCode()); + resp.put("body", response.getBody()); + resp.put("headers", convertHeaders(response.getHeaders())); + } + + BasicDBObject ret = new BasicDBObject(); + ret.put("request", req); + ret.put("response", resp); + + return ret.toString(); + } + + public static String convertHttpRespToOriginalString(HttpResponseParams httpResponseParams) throws JsonProcessingException { + Map m = new HashMap<>(); + HttpRequestParams httpRequestParams = httpResponseParams.getRequestParams(); + + m.put("method", httpRequestParams.getMethod()); + m.put("path", httpRequestParams.getURL()); + m.put("type",httpResponseParams.type); + m.put("requestHeaders", convertHeaders(httpRequestParams.getHeaders())); + + m.put("requestPayload", httpRequestParams.getPayload()); + m.put("akto_vxlan_id", httpRequestParams.getApiCollectionId()); + m.put("statusCode", httpResponseParams.statusCode+""); + m.put("status", httpResponseParams.status); + m.put("responseHeaders", convertHeaders(httpResponseParams.getHeaders())); + + m.put("responsePayload", httpResponseParams.getPayload()); + m.put("time", httpResponseParams.getTime() + ""); + m.put("akto_account_id", httpResponseParams.getAccountId() + ""); + m.put("ip", httpResponseParams.getSourceIP()); + m.put("is_pending", httpResponseParams.getIsPending() + ""); + m.put("source", httpResponseParams.getSource()); + + return mapper.writeValueAsString(m); + } + + public static String convertHeaders(Map> headers) { + Map headerMap = new HashMap<>(); + if (headers == null) return "{}"; + + for (String h: headers.keySet()) { + List values = headers.get(h); + if (values == null) continue; + headerMap.put(h, String.join(";",values)); + } + + try { + return mapper.writeValueAsString(headerMap); + } catch (JsonProcessingException e) { + ; + return "{}"; + } + } +} + diff --git a/apps/api-runtime/src/main/java/com/akto/utils/SampleDataToSTI.java b/apps/api-runtime/src/main/java/com/akto/utils/SampleDataToSTI.java new file mode 100644 index 0000000000..c2f1fb0c1c --- /dev/null +++ b/apps/api-runtime/src/main/java/com/akto/utils/SampleDataToSTI.java @@ -0,0 +1,146 @@ +package com.akto.utils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods.Method; +import com.akto.parsers.HttpCallParser; + +import com.akto.dto.HttpResponseParams; +import com.akto.dto.SensitiveSampleData; +import com.akto.dto.type.APICatalog; +import com.akto.runtime.APICatalogSync; +import com.akto.runtime.URLAggregator; + +public class SampleDataToSTI { + + // url -> method -> response code -> list(singleTypeInfo) + private Map>>> stiList = new HashMap<>(); + private List singleTypeInfos = new ArrayList<>(); + + public SampleDataToSTI(){ + + } + + public void setSampleDataToSTI(List allData) { + + HttpCallParser parse = new HttpCallParser("", 0, 0, 0, true); + for (SampleData sampleData : allData) { + + Method method = sampleData.getId().getMethod(); + String url = sampleData.getId().getUrl(); + List singleTypeInfoPerURL = new ArrayList<>(); + for (String dataString : sampleData.getSamples()) { + singleTypeInfoPerURL.addAll(getSampleDataToSTIUtil(dataString, url,parse)); + } + Map> responseCodeToSTI = new HashMap<>(); + for(SingleTypeInfo singleTypeInfo:singleTypeInfoPerURL){ + if(responseCodeToSTI.containsKey(singleTypeInfo.getResponseCode())){ + responseCodeToSTI.get(singleTypeInfo.getResponseCode()).add(singleTypeInfo); + } + else{ + List temp = new ArrayList<>(); + temp.add(singleTypeInfo); + responseCodeToSTI.put(singleTypeInfo.getResponseCode(),temp); + } + } + if(stiList.containsKey(url)){ + stiList.get(url).put(method.toString(),responseCodeToSTI); + } + else{ + Map>> stiMap = new HashMap<>(); + stiMap.put(method.toString(), responseCodeToSTI); + stiList.put(url,stiMap); + } + singleTypeInfos.addAll(singleTypeInfoPerURL); + } + } + + public void setSensitiveSampleDataToSTI(List allData){ + + HttpCallParser parse = new HttpCallParser("", 0, 0, 0, true); + for (SensitiveSampleData sensitiveSampleData : allData) { + + String method = sensitiveSampleData.getId().getMethod(); + String url = sensitiveSampleData.getId().getUrl(); + List singleTypeInfoPerURL = new ArrayList<>(); + for (String dataString : sensitiveSampleData.getSampleData()) { + singleTypeInfoPerURL.addAll(getSampleDataToSTIUtil(dataString, url,parse)); + } + Map> responseCodeToSTI = new HashMap<>(); + for(SingleTypeInfo singleTypeInfo:singleTypeInfoPerURL){ + if(responseCodeToSTI.containsKey(singleTypeInfo.getResponseCode())){ + responseCodeToSTI.get(singleTypeInfo.getResponseCode()).add(singleTypeInfo); + } + else{ + List temp = new ArrayList<>(); + temp.add(singleTypeInfo); + responseCodeToSTI.put(singleTypeInfo.getResponseCode(),temp); + } + } + if(stiList.containsKey(url)){ + stiList.get(url).put(method.toString(),responseCodeToSTI); + } + else{ + Map>> stiMap = new HashMap<>(); + stiMap.put(method.toString(), responseCodeToSTI); + stiList.put(url,stiMap); + } + singleTypeInfos.addAll(singleTypeInfoPerURL); + } + } + + public Map>>> getSingleTypeInfoMap(){ + return this.stiList; + } + + public List getSingleTypeList(){ + return this.singleTypeInfos; + } + + private List getSampleDataToSTIUtil(String dataString, String url,HttpCallParser parse) { + + List singleTypeInfos = new ArrayList<>(); + + HttpResponseParams httpResponseParams = new HttpResponseParams(); + boolean flag = false; + + try { + httpResponseParams = HttpCallParser.parseKafkaMessage(dataString); + } catch (Exception e) { + flag = true; + + } + + if (flag) { + return singleTypeInfos; + } + + List responseParams = new ArrayList<>(); + responseParams.add(httpResponseParams); + List filteredResponseParams = parse.filterHttpResponseParams(responseParams); + Map aggregatorMap = new HashMap<>(); + parse.setAggregatorMap(aggregatorMap); + parse.aggregate(filteredResponseParams); + aggregatorMap = parse.getAggregatorMap(); + parse.apiCatalogSync = new APICatalogSync("0",0); + for (int apiCollectionId : aggregatorMap.keySet()) { + URLAggregator aggregator = aggregatorMap.get(apiCollectionId); + parse.apiCatalogSync.computeDelta(aggregator, false, apiCollectionId); + for (Integer key : parse.apiCatalogSync.delta.keySet()) { + APICatalog apiCatlog = parse.apiCatalogSync.delta.get(key); + singleTypeInfos.addAll(apiCatlog.getAllTypeInfo()); + } + } + + for (int i = 0; i < singleTypeInfos.size(); i++) { + singleTypeInfos.get(i).setUrl(url); + } + + return singleTypeInfos; + } +} \ No newline at end of file diff --git a/apps/api-runtime/src/main/resources/log_check.json b/apps/api-runtime/src/main/resources/log_check.json new file mode 100644 index 0000000000..bf0e9b47c8 --- /dev/null +++ b/apps/api-runtime/src/main/resources/log_check.json @@ -0,0 +1,247 @@ +[ + {"path":"/api/today","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"null","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Content-Length":"6680","Date":"Sat, 23 Oct 2021 07:11:50 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{},"responsePayload":{"healthyMetrics":[],"needAttentionMetrics":[{"id":1624910181,"name":"New Leads","ownerName":"Avneesh Hota","owner":1612899544,"currValue":492.0,"targetValue":5000.0,"status":1,"favourite":false,"trackingPeriod":2,"dashboardName":"Prospecting","dashboardId":1625483860,"pendingActionItems":[{"id":1624988030,"metrics_meta_id":1624910181,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign asdf","additional_info":"","comments_count":4,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1634712672,"userMentioned":false},{"id":1624988273,"metrics_meta_id":1624910181,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities afewe","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1634714278,"userMentioned":false},{"id":1625000025,"metrics_meta_id":1624910181,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625000152,"metrics_meta_id":1624910181,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false},{"id":1634757750,"metrics_meta_id":1624910181,"creationDate":1634757750,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

helloasdasd

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757750,"userMentioned":false}]},{"id":1624982568,"name":"Customers","ownerName":"Ankita Gupta","owner":1618123456,"currValue":55.0,"targetValue":140.0,"status":1,"favourite":false,"trackingPeriod":1,"dashboardName":"Prospecting","dashboardId":1625483860,"pendingActionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false}]}],"overdueActionItems":null,"pendingActionItems":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973115,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/today","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"null","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"4367","Date":"Sat, 23 Oct 2021 07:12:11 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{},"responsePayload":{"healthyMetrics":[{"id":1625725069,"name":"Transactions","ownerName":"Ankita Gupta","owner":1618123456,"currValue":3573.0,"targetValue":4516.129,"status":0,"favourite":false,"trackingPeriod":1,"dashboardName":"Production","dashboardId":1625226749,"pendingActionItems":[]}],"needAttentionMetrics":[{"id":1624982568,"name":"Customers","ownerName":"Ankita Gupta","owner":1618123456,"currValue":55.0,"targetValue":140.0,"status":1,"favourite":false,"trackingPeriod":1,"dashboardName":"Production","dashboardId":1625226749,"pendingActionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false}]}],"overdueActionItems":null,"pendingActionItems":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973135,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/fetchDataFromMySQL","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988030","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Date":"Sat, 23 Oct 2021 07:12:33 GMT","Content-Type":"text/html; charset=US-ASCII"},"method":"POST","requestPayload":{},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973153,"contentType":"text/html; charset=US-ASCII","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItemActivities","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988030","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Content-Length":"685","Date":"Sat, 23 Oct 2021 07:12:33 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1624988030},"responsePayload":[{"comment":"

This is where we started :)

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634669898,"parent_id":1624988030,"parent_type":"action_item","timestamp":1634669898,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"field":"description","delta":{"old_value":"Start new FB ad campaign","new_value":"Start new FB ad campaign asdf"},"id":1634712673,"parent_id":1624988030,"parent_type":"action_item","timestamp":1634712673,"activity_type":"action_item_update_info","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}}],"ip":"[0:0:0:0:0:0:0:1]","time":1634973155,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988030","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Content-Length":"599","Date":"Sat, 23 Oct 2021 07:12:33 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1624988030},"responsePayload":{"id":1624988030,"metrics_info":{"metric_meta_id":1624910181,"name":"New Leads","description":"this is some dummy description","priority":0,"value":111,"health":0},"action_item":{"id":1624988030,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign asdf","additional_info":"","comments_count":4,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},"watching_preference":0},"ip":"[0:0:0:0:0:0:0:1]","time":1634973157,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/addComment","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988030","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"111","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Content-Length":"349","Date":"Sat, 23 Oct 2021 07:12:48 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"parent_id":1624988030,"comment":"

This is a comment man

","follow_up":false,"parent_type":"action_item"},"responsePayload":{"comment":"

This is a comment man

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634973169,"parent_id":1624988030,"parent_type":"action_item","timestamp":1634973169,"activity_type":"comment","author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"}},"ip":"[0:0:0:0:0:0:0:1]","time":1634973170,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInsights","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/card/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"432","Date":"Sat, 23 Oct 2021 07:13:09 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":[],"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973189,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInputMetrics","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/card/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"444","Date":"Sat, 23 Oct 2021 07:13:09 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211024,"favourite":false,"history":null,"insights":null,"mapMetaToName":{},"mapMetaToTrend":{},"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973195,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/card/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"4202","Date":"Sat, 23 Oct 2021 07:13:09 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false}],"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973195,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardData","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/card/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"872","Date":"Sat, 23 Oct 2021 07:13:09 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":55.0,"dashboardId":1624886875,"dashboardName":"US Growth","description":"Number of customers that we have acquired through various marketing channels","dueDate":0,"endDate":20211024,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":"Customers","ownerId":1618123456,"ownerName":"Ankita Gupta","priority":0,"sourceId":0,"startDate":20211018,"status":1,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":140.0,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973195,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/board","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/card/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"17","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"5237","Date":"Sat, 23 Oct 2021 07:13:09 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"id":1625226749},"responsePayload":{"cards":[{"currValue":55.0,"favourite":false,"id":1624982568,"name":"Customers","owner":1618123456,"ownerName":"Ankita Gupta","priority":0,"status":1,"targetValue":140.0,"trackingPeriod":1,"updateTime":0},{"currValue":3573.0,"favourite":false,"id":1625725069,"name":"Transactions","owner":1618123456,"ownerName":"Ankita Gupta","priority":0,"status":0,"targetValue":4516.129,"trackingPeriod":1,"updateTime":0}],"dashboard":{"author_id":1612899590,"cards":[1625581375,1625584246,1625638340,1625639047,1625639493,1625639732,1625665383,1625686609,1625687253,1625687531,1625725069,1625725669,1625730135,1624982568],"id":1625226749,"last_updated_ts":1625226749,"name":"Production","orgAccessType":"VIEW_ONLY","owner_id":1612899544,"teamAccessType":"EDIT","teamId":1625221629},"id":1625226749,"listCards":[{"id":1624982568,"name":"Customers","ownerName":"Ankita Gupta","owner":1618123456,"currValue":55.0,"targetValue":140.0,"status":1,"favourite":false,"trackingPeriod":1,"pendingActionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false}]},{"id":1625725069,"name":"Transactions","ownerName":"Ankita Gupta","owner":1618123456,"currValue":3573.0,"targetValue":4516.129,"status":0,"favourite":false,"trackingPeriod":1,"pendingActionItems":[]}],"orgAccessType":null,"teamAccessType":null,"teamId":1625221629,"usersInfoMap":{"1612899590":{"accounts":null,"emailValidated":false,"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain","passHash":null,"preferredChannel":"SLACK","salt":null,"signupInfoMap":null}}},"ip":"[0:0:0:0:0:0:0:1]","time":1634973195,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/today","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","Pragma":"no-cache","access-token":"null","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","Cache-Control":"no-cache","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"12271","Date":"Sat, 23 Oct 2021 07:13:25 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{},"responsePayload":{"healthyMetrics":[{"id":1625041456,"name":"Price","ownerName":"Avneesh Hota","owner":1612899544,"currValue":259.0,"targetValue":11.290322,"status":0,"favourite":false,"trackingPeriod":1,"dashboardName":"US Growth","dashboardId":1624886875,"pendingActionItems":[{"id":1625046816,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046965,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046975,"metrics_meta_id":1625041456,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624988273,"userMentioned":false},{"id":1625046985,"metrics_meta_id":1625041456,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625046996,"metrics_meta_id":1625041456,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false}]}],"needAttentionMetrics":[{"id":1624910181,"name":"New Leads","ownerName":"Avneesh Hota","owner":1612899544,"currValue":492.0,"targetValue":5000.0,"status":1,"favourite":true,"trackingPeriod":2,"dashboardName":"US Growth","dashboardId":1624886875,"pendingActionItems":[{"id":1624988030,"metrics_meta_id":1624910181,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign asdf","additional_info":"","comments_count":5,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1634973170,"userMentioned":false},{"id":1624988273,"metrics_meta_id":1624910181,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities afewe","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1634714278,"userMentioned":false},{"id":1625000025,"metrics_meta_id":1624910181,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625000152,"metrics_meta_id":1624910181,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false},{"id":1634757750,"metrics_meta_id":1624910181,"creationDate":1634757750,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

helloasdasd

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757750,"userMentioned":false}]},{"id":1624982568,"name":"Customers","ownerName":"Ankita Gupta","owner":1618123456,"currValue":55.0,"targetValue":140.0,"status":1,"favourite":false,"trackingPeriod":1,"dashboardName":"US Growth","dashboardId":1624886875,"pendingActionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false}]},{"id":1624983442,"name":"Revenue","ownerName":"Chris Harris","owner":1619367945,"currValue":35150.0,"targetValue":500000.0,"status":1,"favourite":true,"trackingPeriod":2,"dashboardName":"US Growth","dashboardId":1624886875,"pendingActionItems":[{"id":1624989233,"metrics_meta_id":1624983442,"creationDate":1624989233,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Enable payment for venmo users","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989233,"userMentioned":false},{"id":1624989326,"metrics_meta_id":1624983442,"creationDate":1624989326,"dueDate":1625140819,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Add new subscription plan","additional_info":"","comments_count":31,"watchers_count":8,"current_status":0,"unread":true,"last_updated_date":1624989326,"userMentioned":false},{"id":1624989537,"metrics_meta_id":1624983442,"creationDate":1624989537,"dueDate":1624190419,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Upsell to power users","additional_info":"","comments_count":6,"watchers_count":2,"current_status":0,"unread":true,"last_updated_date":1624989537,"userMentioned":false},{"id":1625000596,"metrics_meta_id":1624983442,"creationDate":1625000596,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market opportunities","additional_info":"","comments_count":5,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000596,"userMentioned":false},{"id":1634670210,"metrics_meta_id":1624983442,"creationDate":1634670210,"dueDate":1635272999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1622038576,"login":"avneesh@akto.io","name":"avneesh@akto.io"},"priority":0,"description":"Increase revenue from Latin America","additional_info":"

awfwef

","comments_count":1,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757610,"userMentioned":false},{"id":1634757813,"metrics_meta_id":1624983442,"creationDate":1634757813,"dueDate":1635272999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

hellosadsad

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757813,"userMentioned":false}]}],"overdueActionItems":null,"pendingActionItems":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973212,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/toggleFavourite","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"55","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"159","Date":"Sat, 23 Oct 2021 07:13:44 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"preference":0,"parent_id":1624910181,"type":"Metric"},"responsePayload":{"parent_id":1624910181,"preference":0,"type":"Metric","watchers":"SUCCESS","watchersInfo":[{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}]},"ip":"[0:0:0:0:0:0:0:1]","time":1634973225,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/userActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"23","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"833","Date":"Sat, 23 Oct 2021 07:14:02 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"owner_id":1618123456},"responsePayload":[{"id":1624989131,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":false,"userMentioned":false},{"id":1624989233,"creationDate":1624989233,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Enable payment for venmo users","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":false,"userMentioned":false}],"ip":"[0:0:0:0:0:0:0:1]","time":1634973244,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/saveNewActionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"176","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"403","Date":"Sat, 23 Oct 2021 07:14:16 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"metricsMetaId":1624910181,"description":"another action item","assignee":1618123456,"priority":0,"dueDate":1635704999,"follow_up_dates":[],"additional_info":"

hello

"},"responsePayload":{"id":1634973256,"creationDate":1634973256,"dueDate":1635704999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"another action item","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":false,"userMentioned":false},"ip":"[0:0:0:0:0:0:0:1]","time":1634973259,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/fetchDataFromMySQL","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973256","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Date":"Sat, 23 Oct 2021 07:14:29 GMT","Content-Type":"text/html; charset=US-ASCII"},"method":"POST","requestPayload":{},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973269,"contentType":"text/html; charset=US-ASCII","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItemActivities","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973256","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:14:29 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1634973256},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973269,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973256","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"606","Date":"Sat, 23 Oct 2021 07:14:29 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1634973256},"responsePayload":{"id":1634973256,"metrics_info":{"metric_meta_id":1624910181,"name":"New Leads","description":"this is some dummy description","priority":0,"value":111,"health":0},"action_item":{"id":1634973256,"creationDate":1634973256,"dueDate":1635704999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"another action item","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":false,"userMentioned":false},"watching_preference":0},"ip":"[0:0:0:0:0:0:0:1]","time":1634973274,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/toggleFavourite","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"55","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Content-Length":"226","Date":"Sat, 23 Oct 2021 07:15:03 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"preference":1,"parent_id":1624982568,"type":"Metric"},"responsePayload":{"parent_id":1624982568,"preference":1,"type":"Metric","watchers":"SUCCESS","watchersInfo":[{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"}]},"ip":"[0:0:0:0:0:0:0:1]","time":1634973305,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/fetchDataFromMySQL","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988674","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Date":"Sat, 23 Oct 2021 07:15:07 GMT","Content-Type":"text/html; charset=US-ASCII"},"method":"POST","requestPayload":{},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973307,"contentType":"text/html; charset=US-ASCII","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItemActivities","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988674","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Content-Length":"2011","Date":"Sat, 23 Oct 2021 07:15:07 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1624988674},"responsePayload":[{"comment":"

comment 1

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1624997773,"parent_id":1624988674,"parent_type":"action_item","timestamp":1624997773,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"comment":"

comment 2

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1624997780,"parent_id":1624988674,"parent_type":"action_item","timestamp":1624997780,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"comment":"

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1625226796,"parent_id":1624988674,"parent_type":"action_item","timestamp":1625226796,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"comment":"

asdfawefw

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634712044,"parent_id":1624988674,"parent_type":"action_item","timestamp":1634712044,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"comment":"

asdf

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634712526,"parent_id":1624988674,"parent_type":"action_item","timestamp":1634712526,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"comment":"

another one

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634714221,"parent_id":1624988674,"parent_type":"action_item","timestamp":1634714221,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}}],"ip":"[0:0:0:0:0:0:0:1]","time":1634973309,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988674","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Content-Length":"659","Date":"Sat, 23 Oct 2021 07:15:07 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1624988674},"responsePayload":{"id":1624988674,"metrics_info":{"metric_meta_id":1624982568,"name":"Customers","description":"Number of customers that we have acquired through various marketing channels","priority":0,"value":111,"health":0},"action_item":{"id":1624988674,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},"watching_preference":0},"ip":"[0:0:0:0:0:0:0:1]","time":1634973312,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/editComment","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988674","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"60","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMTAsImV4cCI6MTYzNDk3NDAxMH0.ZG2GaHZlu74VKotqUpWlknPP5vXBIdjYFPk5zU9t-g7l6bndHls4aiouBwiO45JrIfLTtm3-u8xdBFYi5tugth4sN06Hpic8KPtHRHmOOdRnZjYj_5wfG2ZzcctvZticT2RTpYeNqS1zwQ0X6hQ2OolQKKPi9zxSHRNKEqExYUebx-GZOnHfGJ7xlZqFmpt5hhdbv0T18OPRcpJlgU5yXVFv9_Hz-9u2UmserphrpTBjDXaMBxP7T44paSmDsArElLJrhvCuegeHvDw0I94EE8C3_7to017VKVAbXsYkAx4txXa7ETIdgLe9YkpSyz_IkXYM3yYazNrjoTZx3lY2Ng","Date":"Sat, 23 Oct 2021 07:16:44 GMT"},"method":"POST","requestPayload":{"comment_id":1634714221,"comment":"

another awefwef

"},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973404,"contentType":null,"cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":401} +, +{"path":"/api/fetchDataFromMySQL","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988674","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"null","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Date":"Sat, 23 Oct 2021 07:16:57 GMT","Content-Type":"text/html; charset=US-ASCII"},"method":"POST","requestPayload":{},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973418,"contentType":"text/html; charset=US-ASCII","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItemActivities","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988674","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"null","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"2011","Date":"Sat, 23 Oct 2021 07:16:57 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1624988674},"responsePayload":[{"comment":"

comment 1

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1624997773,"parent_id":1624988674,"parent_type":"action_item","timestamp":1624997773,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"comment":"

comment 2

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1624997780,"parent_id":1624988674,"parent_type":"action_item","timestamp":1624997780,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"comment":"

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1625226796,"parent_id":1624988674,"parent_type":"action_item","timestamp":1625226796,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"comment":"

asdfawefw

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634712044,"parent_id":1624988674,"parent_type":"action_item","timestamp":1634712044,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"comment":"

asdf

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634712526,"parent_id":1624988674,"parent_type":"action_item","timestamp":1634712526,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},{"comment":"

another one

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634714221,"parent_id":1624988674,"parent_type":"action_item","timestamp":1634714221,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}}],"ip":"[0:0:0:0:0:0:0:1]","time":1634973420,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1624988674","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"null","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"660","Date":"Sat, 23 Oct 2021 07:16:57 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1624988674},"responsePayload":{"id":1624988674,"metrics_info":{"metric_meta_id":1624982568,"name":"Customers","description":"Number of customers that we have acquired through various marketing channels","priority":0,"value":111,"health":0},"action_item":{"id":1624988674,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":false,"userMentioned":false},"watching_preference":0},"ip":"[0:0:0:0:0:0:0:1]","time":1634973421,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInsights","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860/card/1624910181","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"432","Date":"Sat, 23 Oct 2021 07:17:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624910181},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624910181,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":[],"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973425,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860/card/1624910181","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"3211","Date":"Sat, 23 Oct 2021 07:17:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624910181},"responsePayload":{"action":null,"actionItemId":0,"actionItems":[{"id":1624988030,"metrics_meta_id":1624910181,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign asdf","additional_info":"","comments_count":5,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1634973170,"userMentioned":false},{"id":1624988273,"metrics_meta_id":1624910181,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities afewe","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1634714278,"userMentioned":false},{"id":1625000025,"metrics_meta_id":1624910181,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625000152,"metrics_meta_id":1624910181,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false},{"id":1634757750,"metrics_meta_id":1624910181,"creationDate":1634757750,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

helloasdasd

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757750,"userMentioned":false},{"id":1634973256,"metrics_meta_id":1624910181,"creationDate":1634973256,"dueDate":1635704999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"another action item","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973256,"userMentioned":false}],"assignee":null,"cardId":1624910181,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973426,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInputMetrics","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860/card/1624910181","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"444","Date":"Sat, 23 Oct 2021 07:17:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624910181},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624910181,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211031,"favourite":false,"history":null,"insights":null,"mapMetaToName":{},"mapMetaToTrend":{},"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20211001,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":2,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973426,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardData","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860/card/1624910181","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"831","Date":"Sat, 23 Oct 2021 07:17:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624910181},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624910181,"currValue":492.0,"dashboardId":1624886875,"dashboardName":"US Growth","description":"this is some dummy description","dueDate":0,"endDate":20211031,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":"New Leads","ownerId":1612899544,"ownerName":"Avneesh Hota","priority":0,"sourceId":0,"startDate":20211001,"status":1,"targetPlan":{"author_id":1612899590,"creationDate":1624910181,"id":1624910181,"last_updated_by_id":0,"last_updated_time":1624910181,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":5000.0},{"endDate":20210731,"startDate":20210701,"value":5000.0},{"endDate":20210831,"startDate":20210801,"value":5000.0}]},"targetValue":5000.0,"trackingPeriod":2,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973426,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/board","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860/card/1624910181","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"17","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"7871","Date":"Sat, 23 Oct 2021 07:17:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"id":1625483860},"responsePayload":{"cards":[{"currValue":492.0,"favourite":false,"id":1624910181,"name":"New Leads","owner":1612899544,"ownerName":"Avneesh Hota","priority":0,"status":1,"targetValue":5000.0,"trackingPeriod":2,"updateTime":0},{"currValue":55.0,"favourite":true,"id":1624982568,"name":"Customers","owner":1618123456,"ownerName":"Ankita Gupta","priority":0,"status":1,"targetValue":140.0,"trackingPeriod":1,"updateTime":0}],"dashboard":{"author_id":1612899590,"cards":[1624910181,1624982568],"id":1625483860,"last_updated_ts":1625483860,"name":"Prospecting","orgAccessType":"VIEW_ONLY","owner_id":1618123456,"teamAccessType":"EDIT","teamId":1625483832},"id":1625483860,"listCards":[{"id":1624910181,"name":"New Leads","ownerName":"Avneesh Hota","owner":1612899544,"currValue":492.0,"targetValue":5000.0,"status":1,"favourite":false,"trackingPeriod":2,"pendingActionItems":[{"id":1624988030,"metrics_meta_id":1624910181,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign asdf","additional_info":"","comments_count":5,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1634973170,"userMentioned":false},{"id":1624988273,"metrics_meta_id":1624910181,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities afewe","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1634714278,"userMentioned":false},{"id":1625000025,"metrics_meta_id":1624910181,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625000152,"metrics_meta_id":1624910181,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false},{"id":1634757750,"metrics_meta_id":1624910181,"creationDate":1634757750,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

helloasdasd

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757750,"userMentioned":false},{"id":1634973256,"metrics_meta_id":1624910181,"creationDate":1634973256,"dueDate":1635704999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"another action item","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973256,"userMentioned":false}]},{"id":1624982568,"name":"Customers","ownerName":"Ankita Gupta","owner":1618123456,"currValue":55.0,"targetValue":140.0,"status":1,"favourite":true,"trackingPeriod":1,"pendingActionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false}]}],"orgAccessType":null,"teamAccessType":null,"teamId":1625483832,"usersInfoMap":{"1612899590":{"accounts":null,"emailValidated":false,"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain","passHash":null,"preferredChannel":"SLACK","salt":null,"signupInfoMap":null}}},"ip":"[0:0:0:0:0:0:0:1]","time":1634973427,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"756","Date":"Sat, 23 Oct 2021 07:17:12 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624910181,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624910181,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624910181,"id":1624910181,"last_updated_by_id":0,"last_updated_time":1624910181,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":5000.0},{"endDate":20210731,"startDate":20210701,"value":5000.0},{"endDate":20210831,"startDate":20210801,"value":5000.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973433,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"753","Date":"Sat, 23 Oct 2021 07:17:12 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973433,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/addComment","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973256","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"105","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"342","Date":"Sat, 23 Oct 2021 07:17:37 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"parent_id":1634973256,"comment":"

asfawef waefawef

","follow_up":true,"parent_type":"action_item"},"responsePayload":{"comment":"

asfawef waefawef

","edited_comments_history":[],"follow_up":true,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634973457,"parent_id":1634973256,"parent_type":"action_item","timestamp":1634973457,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},"ip":"[0:0:0:0:0:0:0:1]","time":1634973459,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/editComment","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973256","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"54","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"342","Date":"Sat, 23 Oct 2021 07:17:51 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"comment_id":1634973457,"comment":"

asfawef a

"},"responsePayload":{"comment":"

asfawef waefawef

","edited_comments_history":[],"follow_up":true,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634973457,"parent_id":1634973256,"parent_type":"action_item","timestamp":1634973457,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},"ip":"[0:0:0:0:0:0:0:1]","time":1634973472,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInsights","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1624886875/card/1625041456","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"432","Date":"Sat, 23 Oct 2021 07:18:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625041456},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625041456,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":[],"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973485,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1624886875/card/1625041456","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"2737","Date":"Sat, 23 Oct 2021 07:18:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625041456},"responsePayload":{"action":null,"actionItemId":0,"actionItems":[{"id":1625046816,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046965,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046975,"metrics_meta_id":1625041456,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624988273,"userMentioned":false},{"id":1625046985,"metrics_meta_id":1625041456,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625046996,"metrics_meta_id":1625041456,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false}],"assignee":null,"cardId":1625041456,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973486,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInputMetrics","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1624886875/card/1625041456","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"444","Date":"Sat, 23 Oct 2021 07:18:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625041456},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625041456,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211024,"favourite":false,"history":null,"insights":null,"mapMetaToName":{},"mapMetaToTrend":{},"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973486,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardData","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1624886875/card/1625041456","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"717","Date":"Sat, 23 Oct 2021 07:18:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625041456},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625041456,"currValue":259.0,"dashboardId":1624886875,"dashboardName":"US Growth","description":"Average cost of good sold","dueDate":0,"endDate":20211024,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":"Price","ownerId":1612899544,"ownerName":"Avneesh Hota","priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1625638340,"id":1625638340,"last_updated_by_id":0,"last_updated_time":1625638340,"targetInstanceList":[{"endDate":20210731,"startDate":20210701,"value":50.0}]},"targetValue":11.29032258064516,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973486,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/board","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1624886875/card/1625041456","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"17","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"13780","Date":"Sat, 23 Oct 2021 07:18:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"id":1624886875},"responsePayload":{"cards":[{"currValue":492.0,"favourite":false,"id":1624910181,"name":"New Leads","owner":1612899544,"ownerName":"Avneesh Hota","priority":0,"status":1,"targetValue":5000.0,"trackingPeriod":2,"updateTime":0},{"currValue":55.0,"favourite":false,"id":1624982568,"name":"Customers","owner":1618123456,"ownerName":"Ankita Gupta","priority":0,"status":1,"targetValue":140.0,"trackingPeriod":1,"updateTime":0},{"currValue":35150.0,"favourite":true,"id":1624983442,"name":"Revenue","owner":1619367945,"ownerName":"Chris Harris","priority":0,"status":1,"targetValue":500000.0,"trackingPeriod":2,"updateTime":0},{"currValue":259.0,"favourite":false,"id":1625041456,"name":"Price","owner":1612899544,"ownerName":"Avneesh Hota","priority":0,"status":0,"targetValue":11.290322,"trackingPeriod":1,"updateTime":0}],"dashboard":{"author_id":1612899590,"cards":[1624910181,1624982568,1624983442,1625041456],"id":1624886875,"last_updated_ts":1624886875,"name":"US Growth","orgAccessType":"VIEW_ONLY","owner_id":1612899590,"teamAccessType":"EDIT","teamId":1625221629},"id":1624886875,"listCards":[{"id":1624910181,"name":"New Leads","ownerName":"Avneesh Hota","owner":1612899544,"currValue":492.0,"targetValue":5000.0,"status":1,"favourite":false,"trackingPeriod":2,"pendingActionItems":[{"id":1624988030,"metrics_meta_id":1624910181,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign asdf","additional_info":"","comments_count":5,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1634973170,"userMentioned":false},{"id":1624988273,"metrics_meta_id":1624910181,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities afewe","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1634714278,"userMentioned":false},{"id":1625000025,"metrics_meta_id":1624910181,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625000152,"metrics_meta_id":1624910181,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false},{"id":1634757750,"metrics_meta_id":1624910181,"creationDate":1634757750,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

helloasdasd

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757750,"userMentioned":false},{"id":1634973256,"metrics_meta_id":1624910181,"creationDate":1634973256,"dueDate":1635704999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"another action item","additional_info":"

hello

","comments_count":1,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973458,"userMentioned":false}]},{"id":1624982568,"name":"Customers","ownerName":"Ankita Gupta","owner":1618123456,"currValue":55.0,"targetValue":140.0,"status":1,"favourite":false,"trackingPeriod":1,"pendingActionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false}]},{"id":1624983442,"name":"Revenue","ownerName":"Chris Harris","owner":1619367945,"currValue":35150.0,"targetValue":500000.0,"status":1,"favourite":true,"trackingPeriod":2,"pendingActionItems":[{"id":1624989233,"metrics_meta_id":1624983442,"creationDate":1624989233,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Enable payment for venmo users","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989233,"userMentioned":false},{"id":1624989326,"metrics_meta_id":1624983442,"creationDate":1624989326,"dueDate":1625140819,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Add new subscription plan","additional_info":"","comments_count":31,"watchers_count":8,"current_status":0,"unread":true,"last_updated_date":1624989326,"userMentioned":false},{"id":1624989537,"metrics_meta_id":1624983442,"creationDate":1624989537,"dueDate":1624190419,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Upsell to power users","additional_info":"","comments_count":6,"watchers_count":2,"current_status":0,"unread":true,"last_updated_date":1624989537,"userMentioned":false},{"id":1625000596,"metrics_meta_id":1624983442,"creationDate":1625000596,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market opportunities","additional_info":"","comments_count":5,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000596,"userMentioned":false},{"id":1634670210,"metrics_meta_id":1624983442,"creationDate":1634670210,"dueDate":1635272999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1622038576,"login":"avneesh@akto.io","name":"avneesh@akto.io"},"priority":0,"description":"Increase revenue from Latin America","additional_info":"

awfwef

","comments_count":1,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757610,"userMentioned":false},{"id":1634757813,"metrics_meta_id":1624983442,"creationDate":1634757813,"dueDate":1635272999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

hellosadsad

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757813,"userMentioned":false}]},{"id":1625041456,"name":"Price","ownerName":"Avneesh Hota","owner":1612899544,"currValue":259.0,"targetValue":11.290322,"status":0,"favourite":false,"trackingPeriod":1,"pendingActionItems":[{"id":1625046816,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046965,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046975,"metrics_meta_id":1625041456,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624988273,"userMentioned":false},{"id":1625046985,"metrics_meta_id":1625041456,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625046996,"metrics_meta_id":1625041456,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false}]}],"orgAccessType":null,"teamAccessType":null,"teamId":1625221629,"usersInfoMap":{"1612899590":{"accounts":null,"emailValidated":false,"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain","passHash":null,"preferredChannel":"SLACK","salt":null,"signupInfoMap":null}}},"ip":"[0:0:0:0:0:0:0:1]","time":1634973488,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/board","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"17","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"5237","Date":"Sat, 23 Oct 2021 07:18:08 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"id":1625226749},"responsePayload":{"cards":[{"currValue":55.0,"favourite":false,"id":1624982568,"name":"Customers","owner":1618123456,"ownerName":"Ankita Gupta","priority":0,"status":1,"targetValue":140.0,"trackingPeriod":1,"updateTime":0},{"currValue":3573.0,"favourite":false,"id":1625725069,"name":"Transactions","owner":1618123456,"ownerName":"Ankita Gupta","priority":0,"status":0,"targetValue":4516.129,"trackingPeriod":1,"updateTime":0}],"dashboard":{"author_id":1612899590,"cards":[1625581375,1625584246,1625638340,1625639047,1625639493,1625639732,1625665383,1625686609,1625687253,1625687531,1625725069,1625725669,1625730135,1624982568],"id":1625226749,"last_updated_ts":1625226749,"name":"Production","orgAccessType":"VIEW_ONLY","owner_id":1612899544,"teamAccessType":"EDIT","teamId":1625221629},"id":1625226749,"listCards":[{"id":1624982568,"name":"Customers","ownerName":"Ankita Gupta","owner":1618123456,"currValue":55.0,"targetValue":140.0,"status":1,"favourite":false,"trackingPeriod":1,"pendingActionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false}]},{"id":1625725069,"name":"Transactions","ownerName":"Ankita Gupta","owner":1618123456,"currValue":3573.0,"targetValue":4516.129,"status":0,"favourite":false,"trackingPeriod":1,"pendingActionItems":[]}],"orgAccessType":null,"teamAccessType":null,"teamId":1625221629,"usersInfoMap":{"1612899590":{"accounts":null,"emailValidated":false,"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain","passHash":null,"preferredChannel":"SLACK","salt":null,"signupInfoMap":null}}},"ip":"[0:0:0:0:0:0:0:1]","time":1634973491,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"643","Date":"Sat, 23 Oct 2021 07:18:12 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625725069,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1625725069,"id":1625725069,"last_updated_by_id":0,"last_updated_time":1625725069,"targetInstanceList":[{"endDate":20210731,"startDate":20210701,"value":20000.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973493,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"753","Date":"Sat, 23 Oct 2021 07:18:12 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973493,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInsights","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/cards/1625725069","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"432","Date":"Sat, 23 Oct 2021 07:18:16 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625725069,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":[],"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973496,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/cards/1625725069","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"432","Date":"Sat, 23 Oct 2021 07:18:16 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069},"responsePayload":{"action":null,"actionItemId":0,"actionItems":[],"assignee":null,"cardId":1625725069,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973497,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInputMetrics","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/cards/1625725069","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"444","Date":"Sat, 23 Oct 2021 07:18:16 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625725069,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211024,"favourite":false,"history":null,"insights":null,"mapMetaToName":{},"mapMetaToTrend":{},"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973497,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardData","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/cards/1625725069","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"734","Date":"Sat, 23 Oct 2021 07:18:16 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625725069,"currValue":3573.0,"dashboardId":1625226749,"dashboardName":"Production","description":"this is some dummy description","dueDate":0,"endDate":20211024,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":"Transactions","ownerId":1618123456,"ownerName":"Ankita Gupta","priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1625725069,"id":1625725069,"last_updated_by_id":0,"last_updated_time":1625725069,"targetInstanceList":[{"endDate":20210731,"startDate":20210701,"value":20000.0}]},"targetValue":4516.129032258064,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973498,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"643","Date":"Sat, 23 Oct 2021 07:18:19 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625725069,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1625725069,"id":1625725069,"last_updated_by_id":0,"last_updated_time":1625725069,"targetInstanceList":[{"endDate":20210731,"startDate":20210701,"value":20000.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973500,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"753","Date":"Sat, 23 Oct 2021 07:18:19 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973500,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"6738","Date":"Sat, 23 Oct 2021 07:18:24 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069,"startDate":20210101,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625725069,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{"2021-04-17":{"fetchTimestamp":1625725071,"updateTime":1618626337,"value":{"number":892.0}},"2021-04-14":{"fetchTimestamp":1625725071,"updateTime":1618422014,"value":{"number":1531.0}},"2021-04-13":{"fetchTimestamp":1625725071,"updateTime":1618317223,"value":{"number":1178.0}},"2021-04-12":{"fetchTimestamp":1625725071,"updateTime":1618244419,"value":{"number":1200.0}},"2021-04-10":{"fetchTimestamp":1625725071,"updateTime":1618059278,"value":{"number":894.0}},"2021-05-31":{"fetchTimestamp":1625725071,"updateTime":1622457846,"value":{"number":637.0}},"2021-06-01":{"fetchTimestamp":1625725071,"updateTime":1622522676,"value":{"number":979.0}},"2021-06-02":{"fetchTimestamp":1625725071,"updateTime":1622608657,"value":{"number":449.0}},"2021-06-04":{"fetchTimestamp":1625725071,"updateTime":1622826930,"value":{"number":520.0}},"2021-04-09":{"fetchTimestamp":1625725071,"updateTime":1617966377,"value":{"number":301.0}},"2021-06-06":{"fetchTimestamp":1625725071,"updateTime":1622973917,"value":{"number":1875.0}},"2021-04-08":{"fetchTimestamp":1625725071,"updateTime":1617860674,"value":{"number":1104.0}},"2021-06-08":{"fetchTimestamp":1625725071,"updateTime":1623130824,"value":{"number":1867.0}},"2021-06-09":{"fetchTimestamp":1625725071,"updateTime":1623259660,"value":{"number":977.0}},"2021-06-10":{"fetchTimestamp":1625725071,"updateTime":1623324190,"value":{"number":1311.0}},"2021-06-11":{"fetchTimestamp":1625725071,"updateTime":1623371167,"value":{"number":537.0}},"2021-06-12":{"fetchTimestamp":1625725071,"updateTime":1623513816,"value":{"number":1400.0}},"2021-05-01":{"fetchTimestamp":1625725071,"updateTime":1619849073,"value":{"number":1462.0}},"2021-06-13":{"fetchTimestamp":1625725071,"updateTime":1623587729,"value":{"number":271.0}},"2021-06-14":{"fetchTimestamp":1625725071,"updateTime":1623635849,"value":{"number":769.0}},"2021-05-03":{"fetchTimestamp":1625725071,"updateTime":1620038798,"value":{"number":791.0}},"2021-06-15":{"fetchTimestamp":1625725071,"updateTime":1623705323,"value":{"number":718.0}},"2021-05-04":{"fetchTimestamp":1625725071,"updateTime":1620120927,"value":{"number":1191.0}},"2021-06-16":{"fetchTimestamp":1625725071,"updateTime":1623803271,"value":{"number":531.0}},"2021-05-05":{"fetchTimestamp":1625725071,"updateTime":1620228988,"value":{"number":1498.0}},"2021-05-06":{"fetchTimestamp":1625725071,"updateTime":1620291030,"value":{"number":784.0}},"2021-06-18":{"fetchTimestamp":1625725071,"updateTime":1623994155,"value":{"number":519.0}},"2021-05-07":{"fetchTimestamp":1625725071,"updateTime":1620372113,"value":{"number":146.0}},"2021-06-19":{"fetchTimestamp":1625725071,"updateTime":1624088299,"value":{"number":643.0}},"2021-05-08":{"fetchTimestamp":1625725071,"updateTime":1620493431,"value":{"number":2232.0}},"2021-05-09":{"fetchTimestamp":1625725071,"updateTime":1620566186,"value":{"number":207.0}},"2021-04-30":{"fetchTimestamp":1625725071,"updateTime":1619774794,"value":{"number":748.0}},"2021-06-20":{"fetchTimestamp":1625725071,"updateTime":1624159458,"value":{"number":883.0}},"2021-06-21":{"fetchTimestamp":1625725071,"updateTime":1624293983,"value":{"number":1662.0}},"2021-05-10":{"fetchTimestamp":1625725071,"updateTime":1620629256,"value":{"number":539.0}},"2021-06-22":{"fetchTimestamp":1625725071,"updateTime":1624361990,"value":{"number":1054.0}},"2021-05-11":{"fetchTimestamp":1625725071,"updateTime":1620703233,"value":{"number":794.0}},"2021-05-12":{"fetchTimestamp":1625725071,"updateTime":1620819239,"value":{"number":653.0}},"2021-06-24":{"fetchTimestamp":1625725071,"updateTime":1624510774,"value":{"number":456.0}},"2021-06-25":{"fetchTimestamp":1625725071,"updateTime":1624637879,"value":{"number":382.0}},"2021-05-14":{"fetchTimestamp":1625725071,"updateTime":1620968916,"value":{"number":1341.0}},"2021-05-15":{"fetchTimestamp":1625725071,"updateTime":1621076721,"value":{"number":796.0}},"2021-06-27":{"fetchTimestamp":1625725071,"updateTime":1624777690,"value":{"number":1394.0}},"2021-05-17":{"fetchTimestamp":1625725071,"updateTime":1621273185,"value":{"number":808.0}},"2021-06-29":{"fetchTimestamp":1625725071,"updateTime":1624930555,"value":{"number":801.0}},"2021-04-29":{"fetchTimestamp":1625725071,"updateTime":1619712734,"value":{"number":1789.0}},"2021-05-18":{"fetchTimestamp":1625725071,"updateTime":1621310180,"value":{"number":702.0}},"2021-04-27":{"fetchTimestamp":1625725071,"updateTime":1619531854,"value":{"number":729.0}},"2021-04-25":{"fetchTimestamp":1625725071,"updateTime":1619360580,"value":{"number":1159.0}},"2021-04-23":{"fetchTimestamp":1625725071,"updateTime":1619194885,"value":{"number":1360.0}},"2021-04-22":{"fetchTimestamp":1625725071,"updateTime":1619102256,"value":{"number":537.0}},"2021-04-20":{"fetchTimestamp":1625725071,"updateTime":1618902412,"value":{"number":609.0}},"2021-06-30":{"fetchTimestamp":1625725071,"updateTime":1625071025,"value":{"number":1177.0}},"2021-05-20":{"fetchTimestamp":1625725071,"updateTime":1621525721,"value":{"number":2366.0}},"2021-05-21":{"fetchTimestamp":1625725071,"updateTime":1621579900,"value":{"number":1204.0}},"2021-05-22":{"fetchTimestamp":1625725071,"updateTime":1621688693,"value":{"number":841.0}},"2021-05-23":{"fetchTimestamp":1625725071,"updateTime":1621752083,"value":{"number":347.0}},"2021-07-05":{"fetchTimestamp":1625725071,"updateTime":1625449596,"value":{"number":411.0}},"2021-07-04":{"fetchTimestamp":1625725071,"updateTime":1625338869,"value":{"number":973.0}},"2021-05-26":{"fetchTimestamp":1625725071,"updateTime":1621991426,"value":{"number":861.0}},"2021-07-07":{"fetchTimestamp":1625725071,"updateTime":1625669993,"value":{"number":1340.0}},"2021-05-27":{"fetchTimestamp":1625725071,"updateTime":1622059685,"value":{"number":851.0}},"2021-07-06":{"fetchTimestamp":1625725071,"updateTime":1625569823,"value":{"number":620.0}},"2021-04-19":{"fetchTimestamp":1625725071,"updateTime":1618811884,"value":{"number":585.0}},"2021-05-29":{"fetchTimestamp":1625725071,"updateTime":1622277083,"value":{"number":173.0}},"2021-07-08":{"fetchTimestamp":1625769261,"updateTime":1625725047,"value":{"number":1202.0}}},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210101,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1625725069,"id":1625725069,"last_updated_by_id":0,"last_updated_time":1625725069,"targetInstanceList":[{"endDate":20210731,"startDate":20210701,"value":20000.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973506,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"17877","Date":"Sat, 23 Oct 2021 07:18:24 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568,"startDate":20210101,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{"2021-01-24":{"fetchTimestamp":1625770242,"updateTime":1611426600,"value":{"number":17.0}},"2021-04-17":{"fetchTimestamp":1625770242,"updateTime":1618597800,"value":{"number":12.0}},"2021-01-25":{"fetchTimestamp":1625770242,"updateTime":1611513000,"value":{"number":7.0}},"2021-04-16":{"fetchTimestamp":1625770242,"updateTime":1618511400,"value":{"number":16.0}},"2021-01-22":{"fetchTimestamp":1625770242,"updateTime":1611253800,"value":{"number":16.0}},"2021-04-15":{"fetchTimestamp":1625770242,"updateTime":1618425000,"value":{"number":13.0}},"2021-01-23":{"fetchTimestamp":1625770242,"updateTime":1611340200,"value":{"number":7.0}},"2021-04-14":{"fetchTimestamp":1625770242,"updateTime":1618338600,"value":{"number":16.0}},"2021-01-20":{"fetchTimestamp":1625770242,"updateTime":1611081000,"value":{"number":7.0}},"2021-04-13":{"fetchTimestamp":1625770242,"updateTime":1618252200,"value":{"number":8.0}},"2021-01-21":{"fetchTimestamp":1625770242,"updateTime":1611167400,"value":{"number":13.0}},"2021-04-12":{"fetchTimestamp":1625770242,"updateTime":1618165800,"value":{"number":20.0}},"2021-04-11":{"fetchTimestamp":1625770242,"updateTime":1618079400,"value":{"number":19.0}},"2021-04-10":{"fetchTimestamp":1625770242,"updateTime":1617993000,"value":{"number":14.0}},"2021-01-19":{"fetchTimestamp":1625770242,"updateTime":1610994600,"value":{"number":5.0}},"2021-01-17":{"fetchTimestamp":1625770242,"updateTime":1610821800,"value":{"number":8.0}},"2021-01-18":{"fetchTimestamp":1625770242,"updateTime":1610908200,"value":{"number":10.0}},"2021-04-09":{"fetchTimestamp":1625770242,"updateTime":1617906600,"value":{"number":11.0}},"2021-01-15":{"fetchTimestamp":1625770242,"updateTime":1610649000,"value":{"number":20.0}},"2021-04-08":{"fetchTimestamp":1625770242,"updateTime":1617820200,"value":{"number":16.0}},"2021-01-16":{"fetchTimestamp":1625770242,"updateTime":1610735400,"value":{"number":5.0}},"2021-04-07":{"fetchTimestamp":1625770242,"updateTime":1617733800,"value":{"number":12.0}},"2021-01-13":{"fetchTimestamp":1625770242,"updateTime":1610476200,"value":{"number":15.0}},"2021-04-06":{"fetchTimestamp":1625770242,"updateTime":1617647400,"value":{"number":20.0}},"2021-01-14":{"fetchTimestamp":1625770242,"updateTime":1610562600,"value":{"number":12.0}},"2021-04-05":{"fetchTimestamp":1625770242,"updateTime":1617561000,"value":{"number":8.0}},"2021-01-11":{"fetchTimestamp":1625770242,"updateTime":1610303400,"value":{"number":5.0}},"2021-04-04":{"fetchTimestamp":1625770242,"updateTime":1617474600,"value":{"number":20.0}},"2021-01-12":{"fetchTimestamp":1625770242,"updateTime":1610389800,"value":{"number":5.0}},"2021-04-03":{"fetchTimestamp":1625770242,"updateTime":1617388200,"value":{"number":20.0}},"2021-04-02":{"fetchTimestamp":1625770242,"updateTime":1617301800,"value":{"number":5.0}},"2021-01-10":{"fetchTimestamp":1625770242,"updateTime":1610217000,"value":{"number":8.0}},"2021-04-01":{"fetchTimestamp":1625770242,"updateTime":1617215400,"value":{"number":6.0}},"2021-03-31":{"fetchTimestamp":1625770242,"updateTime":1617129000,"value":{"number":14.0}},"2021-03-30":{"fetchTimestamp":1625770242,"updateTime":1617042600,"value":{"number":16.0}},"2021-05-01":{"fetchTimestamp":1625770242,"updateTime":1619807400,"value":{"number":20.0}},"2021-01-08":{"fetchTimestamp":1625770242,"updateTime":1610044200,"value":{"number":19.0}},"2021-05-02":{"fetchTimestamp":1625770242,"updateTime":1619893800,"value":{"number":10.0}},"2021-01-09":{"fetchTimestamp":1625770242,"updateTime":1610130600,"value":{"number":5.0}},"2021-05-03":{"fetchTimestamp":1625770242,"updateTime":1619980200,"value":{"number":7.0}},"2021-01-06":{"fetchTimestamp":1625770242,"updateTime":1609871400,"value":{"number":9.0}},"2021-05-04":{"fetchTimestamp":1625770242,"updateTime":1620066600,"value":{"number":6.0}},"2021-01-07":{"fetchTimestamp":1625770242,"updateTime":1609957800,"value":{"number":20.0}},"2021-05-05":{"fetchTimestamp":1625770242,"updateTime":1620153000,"value":{"number":14.0}},"2021-01-04":{"fetchTimestamp":1625770242,"updateTime":1609698600,"value":{"number":18.0}},"2021-03-29":{"fetchTimestamp":1625770242,"updateTime":1616956200,"value":{"number":12.0}},"2021-05-06":{"fetchTimestamp":1625770242,"updateTime":1620239400,"value":{"number":11.0}},"2021-01-05":{"fetchTimestamp":1625770242,"updateTime":1609785000,"value":{"number":16.0}},"2021-03-28":{"fetchTimestamp":1625770242,"updateTime":1616869800,"value":{"number":16.0}},"2021-05-07":{"fetchTimestamp":1625770242,"updateTime":1620325800,"value":{"number":12.0}},"2021-02-14":{"fetchTimestamp":1625770242,"updateTime":1613241000,"value":{"number":7.0}},"2021-02-15":{"fetchTimestamp":1625770242,"updateTime":1613327400,"value":{"number":14.0}},"2021-02-12":{"fetchTimestamp":1625770242,"updateTime":1613068200,"value":{"number":5.0}},"2021-02-13":{"fetchTimestamp":1625770242,"updateTime":1613154600,"value":{"number":7.0}},"2021-02-10":{"fetchTimestamp":1625770242,"updateTime":1612895400,"value":{"number":5.0}},"2021-02-11":{"fetchTimestamp":1625770242,"updateTime":1612981800,"value":{"number":13.0}},"2021-04-30":{"fetchTimestamp":1625770242,"updateTime":1619721000,"value":{"number":12.0}},"2021-06-20":{"fetchTimestamp":1625770242,"updateTime":1624127400,"value":{"number":15.0}},"2021-06-21":{"fetchTimestamp":1625770242,"updateTime":1624213800,"value":{"number":20.0}},"2021-06-22":{"fetchTimestamp":1625770242,"updateTime":1624300200,"value":{"number":7.0}},"2021-06-23":{"fetchTimestamp":1625770242,"updateTime":1624386600,"value":{"number":5.0}},"2021-06-24":{"fetchTimestamp":1625770242,"updateTime":1624473000,"value":{"number":17.0}},"2021-02-09":{"fetchTimestamp":1625770242,"updateTime":1612809000,"value":{"number":14.0}},"2021-06-25":{"fetchTimestamp":1625770242,"updateTime":1624559400,"value":{"number":16.0}},"2021-06-26":{"fetchTimestamp":1625770242,"updateTime":1624645800,"value":{"number":6.0}},"2021-02-07":{"fetchTimestamp":1625770242,"updateTime":1612636200,"value":{"number":14.0}},"2021-06-27":{"fetchTimestamp":1625770242,"updateTime":1624732200,"value":{"number":7.0}},"2021-02-08":{"fetchTimestamp":1625770242,"updateTime":1612722600,"value":{"number":18.0}},"2021-06-28":{"fetchTimestamp":1625770242,"updateTime":1624818600,"value":{"number":14.0}},"2021-02-05":{"fetchTimestamp":1625770242,"updateTime":1612463400,"value":{"number":8.0}},"2021-06-29":{"fetchTimestamp":1625770242,"updateTime":1624905000,"value":{"number":20.0}},"2021-02-06":{"fetchTimestamp":1625770242,"updateTime":1612549800,"value":{"number":17.0}},"2021-04-29":{"fetchTimestamp":1625770242,"updateTime":1619634600,"value":{"number":10.0}},"2021-02-03":{"fetchTimestamp":1625770242,"updateTime":1612290600,"value":{"number":7.0}},"2021-04-28":{"fetchTimestamp":1625770242,"updateTime":1619548200,"value":{"number":18.0}},"2021-02-04":{"fetchTimestamp":1625770242,"updateTime":1612377000,"value":{"number":11.0}},"2021-04-27":{"fetchTimestamp":1625770242,"updateTime":1619461800,"value":{"number":5.0}},"2021-02-01":{"fetchTimestamp":1625770242,"updateTime":1612117800,"value":{"number":12.0}},"2021-04-26":{"fetchTimestamp":1625770242,"updateTime":1619375400,"value":{"number":6.0}},"2021-02-02":{"fetchTimestamp":1625770242,"updateTime":1612204200,"value":{"number":13.0}},"2021-04-25":{"fetchTimestamp":1625770242,"updateTime":1619289000,"value":{"number":14.0}},"2021-01-31":{"fetchTimestamp":1625770242,"updateTime":1612031400,"value":{"number":20.0}},"2021-04-24":{"fetchTimestamp":1625770242,"updateTime":1619202600,"value":{"number":12.0}},"2021-04-23":{"fetchTimestamp":1625770242,"updateTime":1619116200,"value":{"number":14.0}},"2021-04-22":{"fetchTimestamp":1625770242,"updateTime":1619029800,"value":{"number":18.0}},"2021-01-30":{"fetchTimestamp":1625770242,"updateTime":1611945000,"value":{"number":8.0}},"2021-04-21":{"fetchTimestamp":1625770242,"updateTime":1618943400,"value":{"number":15.0}},"2021-04-20":{"fetchTimestamp":1625770242,"updateTime":1618857000,"value":{"number":16.0}},"2021-06-30":{"fetchTimestamp":1625770242,"updateTime":1624991400,"value":{"number":10.0}},"2021-07-01":{"fetchTimestamp":1625770242,"updateTime":1625077800,"value":{"number":7.0}},"2021-07-03":{"fetchTimestamp":1625770242,"updateTime":1625250600,"value":{"number":14.0}},"2021-07-02":{"fetchTimestamp":1625770242,"updateTime":1625164200,"value":{"number":6.0}},"2021-07-05":{"fetchTimestamp":1625770242,"updateTime":1625423400,"value":{"number":12.0}},"2021-07-04":{"fetchTimestamp":1625770242,"updateTime":1625337000,"value":{"number":11.0}},"2021-01-28":{"fetchTimestamp":1625770242,"updateTime":1611772200,"value":{"number":14.0}},"2021-07-07":{"fetchTimestamp":1625770242,"updateTime":1625596200,"value":{"number":5.0}},"2021-01-29":{"fetchTimestamp":1625770242,"updateTime":1611858600,"value":{"number":7.0}},"2021-07-06":{"fetchTimestamp":1625770242,"updateTime":1625509800,"value":{"number":18.0}},"2021-01-26":{"fetchTimestamp":1625770242,"updateTime":1611599400,"value":{"number":7.0}},"2021-04-19":{"fetchTimestamp":1625770242,"updateTime":1618770600,"value":{"number":14.0}},"2021-01-27":{"fetchTimestamp":1625770242,"updateTime":1611685800,"value":{"number":13.0}},"2021-04-18":{"fetchTimestamp":1625770242,"updateTime":1618684200,"value":{"number":20.0}},"2021-07-08":{"fetchTimestamp":1625770242,"updateTime":1625682600,"value":{"number":20.0}},"2021-03-05":{"fetchTimestamp":1625770242,"updateTime":1614882600,"value":{"number":13.0}},"2021-03-04":{"fetchTimestamp":1625770242,"updateTime":1614796200,"value":{"number":9.0}},"2021-03-03":{"fetchTimestamp":1625770242,"updateTime":1614709800,"value":{"number":16.0}},"2021-03-02":{"fetchTimestamp":1625770242,"updateTime":1614623400,"value":{"number":8.0}},"2021-03-01":{"fetchTimestamp":1625770242,"updateTime":1614537000,"value":{"number":9.0}},"2021-05-30":{"fetchTimestamp":1625770242,"updateTime":1622313000,"value":{"number":9.0}},"2021-05-31":{"fetchTimestamp":1625770242,"updateTime":1622399400,"value":{"number":10.0}},"2021-06-01":{"fetchTimestamp":1625770242,"updateTime":1622485800,"value":{"number":18.0}},"2021-06-02":{"fetchTimestamp":1625770242,"updateTime":1622572200,"value":{"number":19.0}},"2021-06-03":{"fetchTimestamp":1625770242,"updateTime":1622658600,"value":{"number":8.0}},"2021-06-04":{"fetchTimestamp":1625770242,"updateTime":1622745000,"value":{"number":7.0}},"2021-06-05":{"fetchTimestamp":1625770242,"updateTime":1622831400,"value":{"number":20.0}},"2021-06-06":{"fetchTimestamp":1625770242,"updateTime":1622917800,"value":{"number":15.0}},"2021-02-27":{"fetchTimestamp":1625770242,"updateTime":1614364200,"value":{"number":17.0}},"2021-06-07":{"fetchTimestamp":1625770242,"updateTime":1623004200,"value":{"number":7.0}},"2021-02-28":{"fetchTimestamp":1625770242,"updateTime":1614450600,"value":{"number":6.0}},"2021-06-08":{"fetchTimestamp":1625770242,"updateTime":1623090600,"value":{"number":19.0}},"2021-02-25":{"fetchTimestamp":1625770242,"updateTime":1614191400,"value":{"number":14.0}},"2021-06-09":{"fetchTimestamp":1625770242,"updateTime":1623177000,"value":{"number":17.0}},"2021-02-26":{"fetchTimestamp":1625770242,"updateTime":1614277800,"value":{"number":9.0}},"2021-02-23":{"fetchTimestamp":1625770242,"updateTime":1614018600,"value":{"number":5.0}},"2021-02-24":{"fetchTimestamp":1625770242,"updateTime":1614105000,"value":{"number":5.0}},"2021-02-21":{"fetchTimestamp":1625770242,"updateTime":1613845800,"value":{"number":16.0}},"2021-02-22":{"fetchTimestamp":1625770242,"updateTime":1613932200,"value":{"number":9.0}},"2021-02-20":{"fetchTimestamp":1625770242,"updateTime":1613759400,"value":{"number":12.0}},"2021-06-10":{"fetchTimestamp":1625770242,"updateTime":1623263400,"value":{"number":18.0}},"2021-06-11":{"fetchTimestamp":1625770242,"updateTime":1623349800,"value":{"number":8.0}},"2021-06-12":{"fetchTimestamp":1625770242,"updateTime":1623436200,"value":{"number":16.0}},"2021-06-13":{"fetchTimestamp":1625770242,"updateTime":1623522600,"value":{"number":9.0}},"2021-06-14":{"fetchTimestamp":1625770242,"updateTime":1623609000,"value":{"number":9.0}},"2021-06-15":{"fetchTimestamp":1625770242,"updateTime":1623695400,"value":{"number":6.0}},"2021-02-18":{"fetchTimestamp":1625770242,"updateTime":1613586600,"value":{"number":6.0}},"2021-06-16":{"fetchTimestamp":1625770242,"updateTime":1623781800,"value":{"number":10.0}},"2021-02-19":{"fetchTimestamp":1625770242,"updateTime":1613673000,"value":{"number":9.0}},"2021-06-17":{"fetchTimestamp":1625770242,"updateTime":1623868200,"value":{"number":19.0}},"2021-02-16":{"fetchTimestamp":1625770242,"updateTime":1613413800,"value":{"number":20.0}},"2021-06-18":{"fetchTimestamp":1625770242,"updateTime":1623954600,"value":{"number":19.0}},"2021-02-17":{"fetchTimestamp":1625770242,"updateTime":1613500200,"value":{"number":6.0}},"2021-06-19":{"fetchTimestamp":1625770242,"updateTime":1624041000,"value":{"number":7.0}},"2021-01-02":{"fetchTimestamp":1625770242,"updateTime":1609525800,"value":{"number":20.0}},"2021-03-27":{"fetchTimestamp":1625770242,"updateTime":1616783400,"value":{"number":11.0}},"2021-05-08":{"fetchTimestamp":1625770242,"updateTime":1620412200,"value":{"number":18.0}},"2021-01-03":{"fetchTimestamp":1625770242,"updateTime":1609612200,"value":{"number":14.0}},"2021-03-26":{"fetchTimestamp":1625770242,"updateTime":1616697000,"value":{"number":15.0}},"2021-05-09":{"fetchTimestamp":1625770242,"updateTime":1620498600,"value":{"number":5.0}},"2021-03-25":{"fetchTimestamp":1625770242,"updateTime":1616610600,"value":{"number":10.0}},"2021-01-01":{"fetchTimestamp":1625770242,"updateTime":1609439400,"value":{"number":17.0}},"2021-03-24":{"fetchTimestamp":1625770242,"updateTime":1616524200,"value":{"number":7.0}},"2021-03-23":{"fetchTimestamp":1625770242,"updateTime":1616437800,"value":{"number":8.0}},"2021-03-22":{"fetchTimestamp":1625770242,"updateTime":1616351400,"value":{"number":9.0}},"2021-03-21":{"fetchTimestamp":1625770242,"updateTime":1616265000,"value":{"number":11.0}},"2021-03-20":{"fetchTimestamp":1625770242,"updateTime":1616178600,"value":{"number":11.0}},"2021-05-10":{"fetchTimestamp":1625770242,"updateTime":1620585000,"value":{"number":20.0}},"2021-05-11":{"fetchTimestamp":1625770242,"updateTime":1620671400,"value":{"number":14.0}},"2021-05-12":{"fetchTimestamp":1625770242,"updateTime":1620757800,"value":{"number":16.0}},"2021-05-13":{"fetchTimestamp":1625770242,"updateTime":1620844200,"value":{"number":16.0}},"2021-05-14":{"fetchTimestamp":1625770242,"updateTime":1620930600,"value":{"number":5.0}},"2021-05-15":{"fetchTimestamp":1625770242,"updateTime":1621017000,"value":{"number":8.0}},"2021-03-19":{"fetchTimestamp":1625770242,"updateTime":1616092200,"value":{"number":15.0}},"2021-05-16":{"fetchTimestamp":1625770242,"updateTime":1621103400,"value":{"number":6.0}},"2021-03-18":{"fetchTimestamp":1625770242,"updateTime":1616005800,"value":{"number":6.0}},"2021-05-17":{"fetchTimestamp":1625770242,"updateTime":1621189800,"value":{"number":16.0}},"2021-03-17":{"fetchTimestamp":1625770242,"updateTime":1615919400,"value":{"number":20.0}},"2021-05-18":{"fetchTimestamp":1625770242,"updateTime":1621276200,"value":{"number":6.0}},"2021-03-16":{"fetchTimestamp":1625770242,"updateTime":1615833000,"value":{"number":19.0}},"2021-05-19":{"fetchTimestamp":1625770242,"updateTime":1621362600,"value":{"number":15.0}},"2021-03-15":{"fetchTimestamp":1625770242,"updateTime":1615746600,"value":{"number":8.0}},"2021-03-14":{"fetchTimestamp":1625770242,"updateTime":1615660200,"value":{"number":16.0}},"2021-03-13":{"fetchTimestamp":1625770242,"updateTime":1615573800,"value":{"number":12.0}},"2021-03-12":{"fetchTimestamp":1625770242,"updateTime":1615487400,"value":{"number":19.0}},"2021-03-11":{"fetchTimestamp":1625770242,"updateTime":1615401000,"value":{"number":11.0}},"2021-03-10":{"fetchTimestamp":1625770242,"updateTime":1615314600,"value":{"number":9.0}},"2021-05-20":{"fetchTimestamp":1625770242,"updateTime":1621449000,"value":{"number":7.0}},"2021-05-21":{"fetchTimestamp":1625770242,"updateTime":1621535400,"value":{"number":11.0}},"2021-05-22":{"fetchTimestamp":1625770242,"updateTime":1621621800,"value":{"number":5.0}},"2021-05-23":{"fetchTimestamp":1625770242,"updateTime":1621708200,"value":{"number":10.0}},"2021-05-24":{"fetchTimestamp":1625770242,"updateTime":1621794600,"value":{"number":9.0}},"2021-05-25":{"fetchTimestamp":1625770242,"updateTime":1621881000,"value":{"number":6.0}},"2021-03-09":{"fetchTimestamp":1625770242,"updateTime":1615228200,"value":{"number":15.0}},"2021-05-26":{"fetchTimestamp":1625770242,"updateTime":1621967400,"value":{"number":5.0}},"2021-03-08":{"fetchTimestamp":1625770242,"updateTime":1615141800,"value":{"number":6.0}},"2021-05-27":{"fetchTimestamp":1625770242,"updateTime":1622053800,"value":{"number":9.0}},"2021-03-07":{"fetchTimestamp":1625770242,"updateTime":1615055400,"value":{"number":11.0}},"2021-05-28":{"fetchTimestamp":1625770242,"updateTime":1622140200,"value":{"number":6.0}},"2021-03-06":{"fetchTimestamp":1625770242,"updateTime":1614969000,"value":{"number":20.0}},"2021-05-29":{"fetchTimestamp":1625770242,"updateTime":1622226600,"value":{"number":18.0}}},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210101,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973506,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"643","Date":"Sat, 23 Oct 2021 07:18:26 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625725069,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1625725069,"id":1625725069,"last_updated_by_id":0,"last_updated_time":1625725069,"targetInstanceList":[{"endDate":20210731,"startDate":20210701,"value":20000.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973507,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"753","Date":"Sat, 23 Oct 2021 07:18:26 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973507,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getComments","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:18:38 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1634714355},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973519,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getComments","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:18:44 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1624989131},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973524,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getComments","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:18:48 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1624989131},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973528,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/goToAccount","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"27","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMyMDUsImV4cCI6MTYzNDk3NDEwNX0.gukc8c7jmDOo0wW-cBMU8MJ7tCxUQjqxTHU4CXCj1SJar8E0QDGbbBdOlPPhQ4b2fW4Nf3Hd-H9AxA8edCwIQFZxRoyMenn7GVsJLZ90mi4FxBuBTAehlhq9k69z_0VY11CNhxZTvmCJHxo-LotKQRBETG6yvFVs5IRBnktgQM3IWrEuxfRZ7LlXXX-svxIAvLRAWRPeyX2ny-koovlbhQwhQEgpGw7R2q3S_cTM1MZl8N73pfDk4JNaFGmaRKlBPdYB8y5LdiEQwd9qkiIH1BATevxBNtfoDt-Bx0noegk9r3wXqf_7JNVvem7a7wTQSWb494-XvAu8cjOPZT1aRA","Content-Length":"10","Date":"Sat, 23 Oct 2021 07:19:07 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"newAccountId":1613158352},"responsePayload":1613158352,"ip":"[0:0:0:0:0:0:0:1]","time":1634973547,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/today","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"null","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"12735","Date":"Sat, 23 Oct 2021 07:19:09 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{},"responsePayload":{"healthyMetrics":[{"id":1625041456,"name":"Price","ownerName":"Avneesh Hota","owner":1612899544,"currValue":259.0,"targetValue":11.290322,"status":0,"favourite":false,"trackingPeriod":1,"dashboardName":"US Growth","dashboardId":1624886875,"pendingActionItems":[{"id":1625046816,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046965,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046975,"metrics_meta_id":1625041456,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624988273,"userMentioned":false},{"id":1625046985,"metrics_meta_id":1625041456,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625046996,"metrics_meta_id":1625041456,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false}]}],"needAttentionMetrics":[{"id":1624910181,"name":"New Leads","ownerName":"Avneesh Hota","owner":1612899544,"currValue":492.0,"targetValue":5000.0,"status":1,"favourite":false,"trackingPeriod":2,"dashboardName":"US Growth","dashboardId":1624886875,"pendingActionItems":[{"id":1624988030,"metrics_meta_id":1624910181,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign asdf","additional_info":"","comments_count":5,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1634973170,"userMentioned":false},{"id":1624988273,"metrics_meta_id":1624910181,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities afewe","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1634714278,"userMentioned":false},{"id":1625000025,"metrics_meta_id":1624910181,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625000152,"metrics_meta_id":1624910181,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false},{"id":1634757750,"metrics_meta_id":1624910181,"creationDate":1634757750,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

helloasdasd

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757750,"userMentioned":false},{"id":1634973256,"metrics_meta_id":1624910181,"creationDate":1634973256,"dueDate":1635704999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"another action item","additional_info":"

hello

","comments_count":1,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973458,"userMentioned":false}]},{"id":1624982568,"name":"Customers","ownerName":"Ankita Gupta","owner":1618123456,"currValue":55.0,"targetValue":140.0,"status":1,"favourite":false,"trackingPeriod":1,"dashboardName":"US Growth","dashboardId":1624886875,"pendingActionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false}]},{"id":1624983442,"name":"Revenue","ownerName":"Chris Harris","owner":1619367945,"currValue":35150.0,"targetValue":500000.0,"status":1,"favourite":true,"trackingPeriod":2,"dashboardName":"US Growth","dashboardId":1624886875,"pendingActionItems":[{"id":1624989233,"metrics_meta_id":1624983442,"creationDate":1624989233,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Enable payment for venmo users","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989233,"userMentioned":false},{"id":1624989326,"metrics_meta_id":1624983442,"creationDate":1624989326,"dueDate":1625140819,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Add new subscription plan","additional_info":"","comments_count":31,"watchers_count":8,"current_status":0,"unread":true,"last_updated_date":1624989326,"userMentioned":false},{"id":1624989537,"metrics_meta_id":1624983442,"creationDate":1624989537,"dueDate":1624190419,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Upsell to power users","additional_info":"","comments_count":6,"watchers_count":2,"current_status":0,"unread":true,"last_updated_date":1624989537,"userMentioned":false},{"id":1625000596,"metrics_meta_id":1624983442,"creationDate":1625000596,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market opportunities","additional_info":"","comments_count":5,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000596,"userMentioned":false},{"id":1634670210,"metrics_meta_id":1624983442,"creationDate":1634670210,"dueDate":1635272999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1622038576,"login":"avneesh@akto.io","name":"avneesh@akto.io"},"priority":0,"description":"Increase revenue from Latin America","additional_info":"

awfwef

","comments_count":1,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757610,"userMentioned":false},{"id":1634757813,"metrics_meta_id":1624983442,"creationDate":1634757813,"dueDate":1635272999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

hellosadsad

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757813,"userMentioned":false}]}],"overdueActionItems":null,"pendingActionItems":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973557,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/fetchDataFromMySQL","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1625000287","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Date":"Sat, 23 Oct 2021 07:19:40 GMT","Content-Type":"text/html; charset=US-ASCII"},"method":"POST","requestPayload":{},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973580,"contentType":"text/html; charset=US-ASCII","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItemActivities","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1625000287","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:19:40 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1625000287},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973581,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1625000287","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"653","Date":"Sat, 23 Oct 2021 07:19:40 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1625000287},"responsePayload":{"id":1625000287,"metrics_info":{"metric_meta_id":1624982568,"name":"Customers","description":"Number of customers that we have acquired through various marketing channels","priority":0,"value":111,"health":0},"action_item":{"id":1625000287,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},"watching_preference":0},"ip":"[0:0:0:0:0:0:0:1]","time":1634973585,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardData","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"734","Date":"Sat, 23 Oct 2021 07:19:56 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625725069,"currValue":3573.0,"dashboardId":1625226749,"dashboardName":"Production","description":"this is some dummy description","dueDate":0,"endDate":20211024,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":"Transactions","ownerId":1618123456,"ownerName":"Ankita Gupta","priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1625725069,"id":1625725069,"last_updated_by_id":0,"last_updated_time":1625725069,"targetInstanceList":[{"endDate":20210731,"startDate":20210701,"value":20000.0}]},"targetValue":4516.129032258064,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973598,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"432","Date":"Sat, 23 Oct 2021 07:19:58 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069},"responsePayload":{"action":null,"actionItemId":0,"actionItems":[],"assignee":null,"cardId":1625725069,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973599,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardData","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"734","Date":"Sat, 23 Oct 2021 07:20:15 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625725069,"currValue":3573.0,"dashboardId":1625226749,"dashboardName":"Production","description":"this is some dummy description","dueDate":0,"endDate":20211024,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":"Transactions","ownerId":1618123456,"ownerName":"Ankita Gupta","priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1625725069,"id":1625725069,"last_updated_by_id":0,"last_updated_time":1625725069,"targetInstanceList":[{"endDate":20210731,"startDate":20210701,"value":20000.0}]},"targetValue":4516.129032258064,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973617,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"432","Date":"Sat, 23 Oct 2021 07:20:17 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069},"responsePayload":{"action":null,"actionItemId":0,"actionItems":[],"assignee":null,"cardId":1625725069,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973618,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/sqlCredentialsBelongingToUser","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"18","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:20:42 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbType":"MYSQL"},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973643,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/sqlCredentialsBelongingToUser","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"18","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"165","Date":"Sat, 23 Oct 2021 07:20:59 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbType":"MYSQL"},"responsePayload":[{"text":"SG-name01-4570-mysql-master.servers.mongodirector.com:3306:-1-user01","value":1625519985},{"text":"remotemysql.com:3306:-1-Nzfbji1X9d","value":1625669556}],"ip":"[0:0:0:0:0:0:0:1]","time":1634973659,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/dbNamesList","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"44","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"35","Date":"Sat, 23 Oct 2021 07:21:01 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":["Nzfbji1X9d","information_schema"],"ip":"[0:0:0:0:0:0:0:1]","time":1634973673,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/sqlCredentialsBelongingToUser","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"18","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"165","Date":"Sat, 23 Oct 2021 07:21:14 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbType":"MYSQL"},"responsePayload":[{"text":"SG-name01-4570-mysql-master.servers.mongodirector.com:3306:-1-user01","value":1625519985},{"text":"remotemysql.com:3306:-1-Nzfbji1X9d","value":1625669556}],"ip":"[0:0:0:0:0:0:0:1]","time":1634973675,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/dbNamesList","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"44","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:21:18 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"thirdPartyId":1625519985,"dbType":"MYSQL"},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973678,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/dbNamesList","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"44","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"35","Date":"Sat, 23 Oct 2021 07:21:23 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":["Nzfbji1X9d","information_schema"],"ip":"[0:0:0:0:0:0:0:1]","time":1634973685,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/tableNamesFromDb","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"74","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"1492","Date":"Sat, 23 Oct 2021 07:21:27 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbName":"information_schema","thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":["CHARACTER_SETS","CLIENT_STATISTICS","COLLATIONS","COLLATION_CHARACTER_SET_APPLICABILITY","COLUMNS","COLUMN_PRIVILEGES","COLUMN_STATISTICS","COMPRESSION_DICTIONARY","COMPRESSION_DICTIONARY_TABLES","ENGINES","EVENTS","FILES","GLOBAL_TEMPORARY_TABLES","INDEX_STATISTICS","INNODB_BUFFER_PAGE","INNODB_BUFFER_PAGE_LRU","INNODB_BUFFER_POOL_STATS","INNODB_CACHED_INDEXES","INNODB_CHANGED_PAGES","INNODB_CMP","INNODB_CMPMEM","INNODB_CMPMEM_RESET","INNODB_CMP_PER_INDEX","INNODB_CMP_PER_INDEX_RESET","INNODB_CMP_RESET","INNODB_COLUMNS","INNODB_DATAFILES","INNODB_FIELDS","INNODB_FOREIGN","INNODB_FOREIGN_COLS","INNODB_FT_BEING_DELETED","INNODB_FT_CONFIG","INNODB_FT_DEFAULT_STOPWORD","INNODB_FT_DELETED","INNODB_FT_INDEX_CACHE","INNODB_FT_INDEX_TABLE","INNODB_INDEXES","INNODB_METRICS","INNODB_SESSION_TEMP_TABLESPACES","INNODB_TABLES","INNODB_TABLESPACES","INNODB_TABLESPACES_BRIEF","INNODB_TABLESPACES_ENCRYPTION","INNODB_TABLESPACES_SCRUBBING","INNODB_TABLESTATS","INNODB_TEMP_TABLE_INFO","INNODB_TRX","INNODB_VIRTUAL","KEYWORDS","KEY_COLUMN_USAGE","OPTIMIZER_TRACE","PARAMETERS","PARTITIONS","PLUGINS","PROCESSLIST","PROFILING","REFERENTIAL_CONSTRAINTS","RESOURCE_GROUPS","ROUTINES","SCHEMATA","SCHEMA_PRIVILEGES","STATISTICS","ST_GEOMETRY_COLUMNS","ST_SPATIAL_REFERENCE_SYSTEMS","TABLES","TABLESPACES","TABLE_CONSTRAINTS","TABLE_PRIVILEGES","TABLE_STATISTICS","TEMPORARY_TABLES","THREAD_STATISTICS","TRIGGERS","USER_PRIVILEGES","USER_STATISTICS","VIEWS","VIEW_ROUTINE_USAGE","VIEW_TABLE_USAGE"],"ip":"[0:0:0:0:0:0:0:1]","time":1634973689,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/sampleRecordsFromTable","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"103","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:21:29 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbName":"information_schema","tableName":"CHARACTER_SETS","thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973689,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/sampleRecordsFromTable","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"106","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:21:31 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbName":"information_schema","tableName":"CLIENT_STATISTICS","thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973691,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/sampleRecordsFromTable","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"99","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:21:32 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbName":"information_schema","tableName":"COLLATIONS","thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973692,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/sampleRecordsFromTable","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"126","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:21:32 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbName":"information_schema","tableName":"COLLATION_CHARACTER_SET_APPLICABILITY","thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973693,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/dbNamesList","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"44","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"35","Date":"Sat, 23 Oct 2021 07:21:38 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":["Nzfbji1X9d","information_schema"],"ip":"[0:0:0:0:0:0:0:1]","time":1634973701,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/tableNamesFromDb","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"66","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"16","Date":"Sat, 23 Oct 2021 07:21:42 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbName":"Nzfbji1X9d","thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":["Transactions"],"ip":"[0:0:0:0:0:0:0:1]","time":1634973704,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/sampleRecordsFromTable","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"93","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:21:44 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbName":"Nzfbji1X9d","tableName":"Transactions","thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973705,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/sampleRecordsFromTable","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/today","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"93","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:21:46 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"dbName":"Nzfbji1X9d","tableName":"Transactions","thirdPartyId":1625669556,"dbType":"MYSQL"},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973706,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/userActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"23","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"5339","Date":"Sat, 23 Oct 2021 07:22:42 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"owner_id":1612899544},"responsePayload":[{"id":1624988674,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":false,"userMentioned":false},{"id":1624988768,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},{"id":1624989326,"creationDate":1624989326,"dueDate":1625140819,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Add new subscription plan","additional_info":"","comments_count":31,"watchers_count":8,"current_status":0,"unread":true,"userMentioned":false},{"id":1624989537,"creationDate":1624989537,"dueDate":1624190419,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Upsell to power users","additional_info":"","comments_count":6,"watchers_count":2,"current_status":0,"unread":true,"userMentioned":false},{"id":1625000025,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},{"id":1625000152,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},{"id":1625000287,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},{"id":1625000437,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},{"id":1625000596,"creationDate":1625000596,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market opportunities","additional_info":"","comments_count":5,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},{"id":1625000744,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},{"id":1625001002,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},{"id":1625046985,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},{"id":1625046996,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false}],"ip":"[0:0:0:0:0:0:0:1]","time":1634973770,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/saveNewActionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"182","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"409","Date":"Sat, 23 Oct 2021 07:22:50 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"metricsMetaId":1624910181,"description":"some","assignee":1612899544,"priority":0,"dueDate":1635272999,"follow_up_dates":[1635272999],"additional_info":"

awfwfea awefawef

"},"responsePayload":{"id":1634973770,"creationDate":1634973770,"dueDate":1635272999,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"some","additional_info":"

awfwfea awefawef

","comments_count":0,"watchers_count":0,"current_status":0,"unread":false,"userMentioned":false},"ip":"[0:0:0:0:0:0:0:1]","time":1634973772,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/fetchDataFromMySQL","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634757750","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Date":"Sat, 23 Oct 2021 07:23:21 GMT","Content-Type":"text/html; charset=US-ASCII"},"method":"POST","requestPayload":{},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973801,"contentType":"text/html; charset=US-ASCII","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItemActivities","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634757750","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:23:21 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1634757750},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973801,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634757750","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"594","Date":"Sat, 23 Oct 2021 07:23:21 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1634757750},"responsePayload":{"id":1634757750,"metrics_info":{"metric_meta_id":1624910181,"name":"New Leads","description":"this is some dummy description","priority":0,"value":111,"health":0},"action_item":{"id":1634757750,"creationDate":1634757750,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

helloasdasd

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"userMentioned":false},"watching_preference":0},"ip":"[0:0:0:0:0:0:0:1]","time":1634973805,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"753","Date":"Sat, 23 Oct 2021 07:23:28 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973809,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"756","Date":"Sat, 23 Oct 2021 07:23:28 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624910181,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624910181,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624910181,"id":1624910181,"last_updated_by_id":0,"last_updated_time":1624910181,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":5000.0},{"endDate":20210731,"startDate":20210701,"value":5000.0},{"endDate":20210831,"startDate":20210801,"value":5000.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973809,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/fetchDataFromMySQL","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973256","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Date":"Sat, 23 Oct 2021 07:23:35 GMT","Content-Type":"text/html; charset=US-ASCII"},"method":"POST","requestPayload":{},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973815,"contentType":"text/html; charset=US-ASCII","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItemActivities","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973256","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"392","Date":"Sat, 23 Oct 2021 07:23:35 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1634973256},"responsePayload":[{"comment":"

asfawef a

","edited_comments_history":[{"edited_comment_id":1634973471,"timestamp":1634973472}],"follow_up":true,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634973457,"parent_id":1634973256,"parent_type":"action_item","timestamp":1634973457,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}}],"ip":"[0:0:0:0:0:0:0:1]","time":1634973816,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973256","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"605","Date":"Sat, 23 Oct 2021 07:23:35 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1634973256},"responsePayload":{"id":1634973256,"metrics_info":{"metric_meta_id":1624910181,"name":"New Leads","description":"this is some dummy description","priority":0,"value":111,"health":0},"action_item":{"id":1634973256,"creationDate":1634973256,"dueDate":1635704999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"another action item","additional_info":"

hello

","comments_count":1,"watchers_count":0,"current_status":0,"unread":true,"userMentioned":false},"watching_preference":0},"ip":"[0:0:0:0:0:0:0:1]","time":1634973821,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"756","Date":"Sat, 23 Oct 2021 07:23:47 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624910181,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624910181,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624910181,"id":1624910181,"last_updated_by_id":0,"last_updated_time":1624910181,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":5000.0},{"endDate":20210731,"startDate":20210701,"value":5000.0},{"endDate":20210831,"startDate":20210801,"value":5000.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973828,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"753","Date":"Sat, 23 Oct 2021 07:23:47 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973828,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/userActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"23","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"5749","Date":"Sat, 23 Oct 2021 07:23:59 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"owner_id":1612899544},"responsePayload":[{"id":1624988674,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":false,"userMentioned":false},{"id":1624988768,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},{"id":1624989326,"creationDate":1624989326,"dueDate":1625140819,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Add new subscription plan","additional_info":"","comments_count":31,"watchers_count":8,"current_status":0,"unread":true,"userMentioned":false},{"id":1624989537,"creationDate":1624989537,"dueDate":1624190419,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Upsell to power users","additional_info":"","comments_count":6,"watchers_count":2,"current_status":0,"unread":true,"userMentioned":false},{"id":1625000025,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},{"id":1625000152,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},{"id":1625000287,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},{"id":1625000437,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},{"id":1625000596,"creationDate":1625000596,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market opportunities","additional_info":"","comments_count":5,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},{"id":1625000744,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},{"id":1625001002,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},{"id":1625046985,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},{"id":1625046996,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"userMentioned":false},{"id":1634973770,"creationDate":1634973770,"dueDate":1635272999,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"some","additional_info":"

awfwfea awefawef

","comments_count":0,"watchers_count":0,"current_status":0,"unread":false,"userMentioned":false}],"ip":"[0:0:0:0:0:0:0:1]","time":1634973849,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/saveNewActionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"162","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM0MTcsImV4cCI6MTYzNDk3NDMxN30.eBQAC1qZTj7UnVeqv7w8KutZu7rvcJ-Yxl6intaBS0RxY6UkUZg5uuR9_v_RdFfgDSbe0VofWz03T47abav-NvwEtbxcMLy3SP4gk8bclT8ihJ8kgYeGdE_8sIGhs-y78XDK3LO8CHx3I0-8swQsEsWVc1NG3e-YRaA9UdmdaRKSQQJ5glzOnZB7i7gHkXz7_LSpDKjWKYMfoZ_nrDmty_iSWhJBuvR2Aj35NTfzJBhAXyjFeQqWLTPzVy_jS-9EsunUpv7ZHNI3w4gtPmusUpI4S6-0EgkLcyZkSWjoF9PXGcIEkl1Ja2pdLWRBK3Hcf9qgYfw6YexAh4UlBcaOyg","Content-Length":"399","Date":"Sat, 23 Oct 2021 07:24:06 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"metricsMetaId":1624982568,"description":"gg","assignee":1612899544,"priority":0,"dueDate":1635704999,"follow_up_dates":[],"additional_info":"

hellwewo

"},"responsePayload":{"id":1634973847,"creationDate":1634973847,"dueDate":1635704999,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"gg","additional_info":"

hellwewo

","comments_count":0,"watchers_count":0,"current_status":0,"unread":false,"userMentioned":false},"ip":"[0:0:0:0:0:0:0:1]","time":1634973849,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/board","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"null","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"17","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Content-Length":"8799","Date":"Sat, 23 Oct 2021 07:24:31 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"id":1625483860},"responsePayload":{"cards":[{"currValue":492.0,"favourite":false,"id":1624910181,"name":"New Leads","owner":1612899544,"ownerName":"Avneesh Hota","priority":0,"status":1,"targetValue":5000.0,"trackingPeriod":2,"updateTime":0},{"currValue":55.0,"favourite":true,"id":1624982568,"name":"Customers","owner":1618123456,"ownerName":"Ankita Gupta","priority":0,"status":1,"targetValue":140.0,"trackingPeriod":1,"updateTime":0}],"dashboard":{"author_id":1612899590,"cards":[1624910181,1624982568],"id":1625483860,"last_updated_ts":1625483860,"name":"Prospecting","orgAccessType":"VIEW_ONLY","owner_id":1618123456,"teamAccessType":"EDIT","teamId":1625483832},"id":1625483860,"listCards":[{"id":1624910181,"name":"New Leads","ownerName":"Avneesh Hota","owner":1612899544,"currValue":492.0,"targetValue":5000.0,"status":1,"favourite":false,"trackingPeriod":2,"pendingActionItems":[{"id":1624988030,"metrics_meta_id":1624910181,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign asdf","additional_info":"","comments_count":5,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1634973170,"userMentioned":false},{"id":1624988273,"metrics_meta_id":1624910181,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities afewe","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1634714278,"userMentioned":false},{"id":1625000025,"metrics_meta_id":1624910181,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625000152,"metrics_meta_id":1624910181,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false},{"id":1634757750,"metrics_meta_id":1624910181,"creationDate":1634757750,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

helloasdasd

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757750,"userMentioned":false},{"id":1634973256,"metrics_meta_id":1624910181,"creationDate":1634973256,"dueDate":1635704999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"another action item","additional_info":"

hello

","comments_count":1,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973458,"userMentioned":false},{"id":1634973770,"metrics_meta_id":1624910181,"creationDate":1634973770,"dueDate":1635272999,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"some","additional_info":"

awfwfea awefawef

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973770,"userMentioned":false}]},{"id":1624982568,"name":"Customers","ownerName":"Ankita Gupta","owner":1618123456,"currValue":55.0,"targetValue":140.0,"status":1,"favourite":true,"trackingPeriod":1,"pendingActionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false},{"id":1634973847,"metrics_meta_id":1624982568,"creationDate":1634973847,"dueDate":1635704999,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"gg","additional_info":"

hellwewo

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973847,"userMentioned":false}]}],"orgAccessType":null,"teamAccessType":null,"teamId":1625483832,"usersInfoMap":{"1612899590":{"accounts":null,"emailValidated":false,"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain","passHash":null,"preferredChannel":"SLACK","salt":null,"signupInfoMap":null}}},"ip":"[0:0:0:0:0:0:0:1]","time":1634973876,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/fetchDataFromMySQL","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1625000437","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Date":"Sat, 23 Oct 2021 07:24:44 GMT","Content-Type":"text/html; charset=US-ASCII"},"method":"POST","requestPayload":{},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973884,"contentType":"text/html; charset=US-ASCII","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItemActivities","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1625000437","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:24:44 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1625000437},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973884,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1625000437","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Content-Length":"667","Date":"Sat, 23 Oct 2021 07:24:44 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1625000437},"responsePayload":{"id":1625000437,"metrics_info":{"metric_meta_id":1624982568,"name":"Customers","description":"Number of customers that we have acquired through various marketing channels","priority":0,"value":111,"health":0},"action_item":{"id":1625000437,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},"watching_preference":0},"ip":"[0:0:0:0:0:0:0:1]","time":1634973888,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Content-Length":"756","Date":"Sat, 23 Oct 2021 07:24:53 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624910181,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624910181,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624910181,"id":1624910181,"last_updated_by_id":0,"last_updated_time":1624910181,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":5000.0},{"endDate":20210731,"startDate":20210701,"value":5000.0},{"endDate":20210831,"startDate":20210801,"value":5000.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973894,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Content-Length":"753","Date":"Sat, 23 Oct 2021 07:24:53 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973894,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"643","Date":"Sat, 23 Oct 2021 07:25:22 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625725069,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625725069,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1625725069,"id":1625725069,"last_updated_by_id":0,"last_updated_time":1625725069,"targetInstanceList":[{"endDate":20210731,"startDate":20210701,"value":20000.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973923,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardTrend","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"61","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"753","Date":"Sat, 23 Oct 2021 07:25:22 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568,"startDate":20210816,"endDate":20211023},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211023,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20210816,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973923,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInsights","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/cards/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"432","Date":"Sat, 23 Oct 2021 07:25:24 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":[],"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973925,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/cards/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"4661","Date":"Sat, 23 Oct 2021 07:25:24 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false},{"id":1634973847,"metrics_meta_id":1624982568,"creationDate":1634973847,"dueDate":1635704999,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"gg","additional_info":"

hellwewo

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973847,"userMentioned":false}],"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973926,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInputMetrics","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/cards/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"444","Date":"Sat, 23 Oct 2021 07:25:24 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211024,"favourite":false,"history":null,"insights":null,"mapMetaToName":{},"mapMetaToTrend":{},"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973926,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardData","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625226749/cards/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"872","Date":"Sat, 23 Oct 2021 07:25:24 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":55.0,"dashboardId":1624886875,"dashboardName":"US Growth","description":"Number of customers that we have acquired through various marketing channels","dueDate":0,"endDate":20211024,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":"Customers","ownerId":1618123456,"ownerName":"Ankita Gupta","priority":0,"sourceId":0,"startDate":20211018,"status":1,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":140.0,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973926,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/fetchDataFromMySQL","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973847","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Date":"Sat, 23 Oct 2021 07:25:30 GMT","Content-Type":"text/html; charset=US-ASCII"},"method":"POST","requestPayload":{},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973930,"contentType":"text/html; charset=US-ASCII","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItemActivities","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973847","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:25:30 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1634973847},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973930,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973847","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"647","Date":"Sat, 23 Oct 2021 07:25:30 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1634973847},"responsePayload":{"id":1634973847,"metrics_info":{"metric_meta_id":1624982568,"name":"Customers","description":"Number of customers that we have acquired through various marketing channels","priority":0,"value":111,"health":0},"action_item":{"id":1634973847,"creationDate":1634973847,"dueDate":1635704999,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"gg","additional_info":"

hellwewo

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"userMentioned":false},"watching_preference":0},"ip":"[0:0:0:0:0:0:0:1]","time":1634973933,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInsights","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860/cards/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Content-Length":"432","Date":"Sat, 23 Oct 2021 07:25:41 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":[],"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973942,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInputMetrics","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860/cards/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Content-Length":"444","Date":"Sat, 23 Oct 2021 07:25:41 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211024,"favourite":false,"history":null,"insights":null,"mapMetaToName":{},"mapMetaToTrend":{},"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973942,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860/cards/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Content-Length":"4662","Date":"Sat, 23 Oct 2021 07:25:41 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":false,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false},{"id":1634973847,"metrics_meta_id":1624982568,"creationDate":1634973847,"dueDate":1635704999,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"gg","additional_info":"

hellwewo

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973847,"userMentioned":false}],"assignee":null,"cardId":1624982568,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973942,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardData","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g; JSESSIONID=node0bfl3ki5qt830qvbet5pb11hb1.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1625483860/cards/1624982568","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua2l0YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM4NzEsImV4cCI6MTYzNDk3NDc3MX0.Ya-h2HHeDBKveP7Yf-k-LiVruzpi3Iht3McHjzJu2t0KU2jDKGrYoX8eLgWe-rDM4DhizPlVJH2ZK0r8nqtvLtGxLqhmGRIoGcwYPAmjBnpJgUwQEmk2GwN59zC7Xzd-h7dfjhHaqtrJH15VfxifDefL-JFpn_GEugYvh2APxuo7sh-269o8b__yCnoynSeawKtcznGB3dwTLeNt4fCFNTyenWEZXrPstzMI1HrPIHT2AmixILLWa4jJv9m0FV8YqtsdZYgqqPe5km89RYCO_-uY93JUgMEHKBtV62r5oIfAuBY5aX_pTl4eb_Jr5hZTZTHIKb_xcwzXoI5Wo6-elw","Content-Length":"871","Date":"Sat, 23 Oct 2021 07:25:41 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1624982568},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1624982568,"currValue":55.0,"dashboardId":1624886875,"dashboardName":"US Growth","description":"Number of customers that we have acquired through various marketing channels","dueDate":0,"endDate":20211024,"favourite":true,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":"Customers","ownerId":1618123456,"ownerName":"Ankita Gupta","priority":0,"sourceId":0,"startDate":20211018,"status":1,"targetPlan":{"author_id":1612899590,"creationDate":1624982567,"id":1624982567,"last_updated_by_id":0,"last_updated_time":1624982567,"targetInstanceList":[{"endDate":20210630,"startDate":20210601,"value":600.0},{"endDate":20210731,"startDate":20210701,"value":620.0},{"endDate":20210831,"startDate":20210801,"value":620.0}]},"targetValue":140.0,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973943,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node0bfl3ki5qt830qvbet5pb11hb1.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/updateActionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw; JSESSIONID=node01mye4vub29fpvsi440zg26hsq0.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1634973847","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"79","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImlhbWF2bmVlc2guaG90YUBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzMxMzEsImV4cCI6MTYzNDk3NDAzMX0.I6s6PusUJby0bDqBHFQBggPSXft7nsR-sQSpp5L6lUCEZRtesvymyeoPN2WQiBtHYJJ4lnCIfMJo9QG9CF9KqxK5OeGtA_jC68EFxQLnAXCX6jnmY2nJap_CWFh3BJrJ1x7UJ2fwZqK1NcmwffvmQvF1LI0jwv0hCBowikPCegKAo7nB3vLrDDIjrek23atQXd7sydxW-pOXegg-JalOX0ASPXm_YwLTeORVZxRtvEP_xQl33pHmFCo3tuoJOnzuxJExzYS5XmS03D49vCgyVc2rbcfzn-0ipHHscYhoBr3JTXwwETvOGOhOqFV45jK60Xi7xrS8D7yqZxZ0UKu0gw","Content-Length":"297","Date":"Sat, 23 Oct 2021 07:25:51 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1634973847,"data":{"field":"description","value":"eawefawf"}},"responsePayload":[{"field":"description","delta":{"old_value":"gg","new_value":"eawefawf"},"id":1634973951,"parent_id":1634973847,"parent_type":"action_item","timestamp":1634973951,"activity_type":"action_item_update_info","author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"}}],"ip":"[0:0:0:0:0:0:0:1]","time":1634973952,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJpYW1hdm5lZXNoLmhvdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcyMDI5LCJleHAiOjE2MzUwNTg0Mjl9.m8qLOYpiBbfwWL0B8eoJ4wSK6XHNqMFQG9d_EETh51S8TuZRk8HUSW04gGnB87EKLMIcubE0XXyTXLIEYMw-_LUx3UOLKYhnqTnMYzJxM24IbWhIHdaRY9wY6WnWuM_Wfk7HcAgfd7nJ9pwmPYXDwANqH12dURivV3zgf0FaCvaHHJhEvIvu0l6cMihAA1z0qIiI0ipfuJgBEvyQmnkjhsA-s4pNhMk0UQqUb1yO3tdvEasRfD_QriC3eSHO6lHxMfYPHtblmsgwkf3kAh6YT3giZjIy7UxOZJiX0ZbmBg7qds_MuKnVf4q5YPnSJ3_YPQ3nHVu63Sjx22m6cfBYqw","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node01mye4vub29fpvsi440zg26hsq0.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInsights","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1624886875/card/1625041456","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"432","Date":"Sat, 23 Oct 2021 07:26:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625041456},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625041456,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":[],"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973965,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardActionItems","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1624886875/card/1625041456","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"2737","Date":"Sat, 23 Oct 2021 07:26:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625041456},"responsePayload":{"action":null,"actionItemId":0,"actionItems":[{"id":1625046816,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046965,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046975,"metrics_meta_id":1625041456,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624988273,"userMentioned":false},{"id":1625046985,"metrics_meta_id":1625041456,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625046996,"metrics_meta_id":1625041456,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false}],"assignee":null,"cardId":1625041456,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":0,"favourite":false,"history":null,"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":0,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":0,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973966,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardInputMetrics","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1624886875/card/1625041456","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"444","Date":"Sat, 23 Oct 2021 07:26:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625041456},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625041456,"currValue":0.0,"dashboardId":0,"dashboardName":null,"description":null,"dueDate":0,"endDate":20211024,"favourite":false,"history":null,"insights":null,"mapMetaToName":{},"mapMetaToTrend":{},"name":null,"ownerId":0,"ownerName":null,"priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":null,"targetValue":0.0,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973966,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/getCardData","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1624886875/card/1625041456","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"21","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"717","Date":"Sat, 23 Oct 2021 07:26:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"cardId":1625041456},"responsePayload":{"action":null,"actionItemId":0,"actionItems":null,"assignee":null,"cardId":1625041456,"currValue":259.0,"dashboardId":1624886875,"dashboardName":"US Growth","description":"Average cost of good sold","dueDate":0,"endDate":20211024,"favourite":false,"history":{},"insights":null,"mapMetaToName":null,"mapMetaToTrend":null,"name":"Price","ownerId":1612899544,"ownerName":"Avneesh Hota","priority":0,"sourceId":0,"startDate":20211018,"status":0,"targetPlan":{"author_id":1612899590,"creationDate":1625638340,"id":1625638340,"last_updated_by_id":0,"last_updated_time":1625638340,"targetInstanceList":[{"endDate":20210731,"startDate":20210701,"value":50.0}]},"targetValue":11.29032258064516,"trackingPeriod":1,"unit":null},"ip":"[0:0:0:0:0:0:0:1]","time":1634973966,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/board","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/boards/1624886875/card/1625041456","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"17","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"14714","Date":"Sat, 23 Oct 2021 07:26:04 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"id":1624886875},"responsePayload":{"cards":[{"currValue":492.0,"favourite":false,"id":1624910181,"name":"New Leads","owner":1612899544,"ownerName":"Avneesh Hota","priority":0,"status":1,"targetValue":5000.0,"trackingPeriod":2,"updateTime":0},{"currValue":55.0,"favourite":false,"id":1624982568,"name":"Customers","owner":1618123456,"ownerName":"Ankita Gupta","priority":0,"status":1,"targetValue":140.0,"trackingPeriod":1,"updateTime":0},{"currValue":35150.0,"favourite":true,"id":1624983442,"name":"Revenue","owner":1619367945,"ownerName":"Chris Harris","priority":0,"status":1,"targetValue":500000.0,"trackingPeriod":2,"updateTime":0},{"currValue":259.0,"favourite":false,"id":1625041456,"name":"Price","owner":1612899544,"ownerName":"Avneesh Hota","priority":0,"status":0,"targetValue":11.290322,"trackingPeriod":1,"updateTime":0}],"dashboard":{"author_id":1612899590,"cards":[1624910181,1624982568,1624983442,1625041456],"id":1624886875,"last_updated_ts":1624886875,"name":"US Growth","orgAccessType":"VIEW_ONLY","owner_id":1612899590,"teamAccessType":"EDIT","teamId":1625221629},"id":1624886875,"listCards":[{"id":1624910181,"name":"New Leads","ownerName":"Avneesh Hota","owner":1612899544,"currValue":492.0,"targetValue":5000.0,"status":1,"favourite":false,"trackingPeriod":2,"pendingActionItems":[{"id":1624988030,"metrics_meta_id":1624910181,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign asdf","additional_info":"","comments_count":5,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1634973170,"userMentioned":false},{"id":1624988273,"metrics_meta_id":1624910181,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities afewe","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1634714278,"userMentioned":false},{"id":1625000025,"metrics_meta_id":1624910181,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625000152,"metrics_meta_id":1624910181,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false},{"id":1634757750,"metrics_meta_id":1624910181,"creationDate":1634757750,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

helloasdasd

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757750,"userMentioned":false},{"id":1634973256,"metrics_meta_id":1624910181,"creationDate":1634973256,"dueDate":1635704999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"another action item","additional_info":"

hello

","comments_count":1,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973458,"userMentioned":false},{"id":1634973770,"metrics_meta_id":1624910181,"creationDate":1634973770,"dueDate":1635272999,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"some","additional_info":"

awfwfea awefawef

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973770,"userMentioned":false}]},{"id":1624982568,"name":"Customers","ownerName":"Ankita Gupta","owner":1618123456,"currValue":55.0,"targetValue":140.0,"status":1,"favourite":false,"trackingPeriod":1,"pendingActionItems":[{"id":1624988674,"metrics_meta_id":1624982568,"creationDate":1624988674,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to new leads","additional_info":"","comments_count":8,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1634714223,"userMentioned":false},{"id":1624988768,"metrics_meta_id":1624982568,"creationDate":1624988768,"dueDate":1624968019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on previously churned accounts","additional_info":"","comments_count":2,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1624988768,"userMentioned":false},{"id":1624989131,"metrics_meta_id":1624982568,"creationDate":1624989131,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Help account manager onboard new customers","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989131,"userMentioned":false},{"id":1625000287,"metrics_meta_id":1624982568,"creationDate":1625000287,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market segment","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000287,"userMentioned":false},{"id":1625000437,"metrics_meta_id":1624982568,"creationDate":1625000437,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Write more blogs explaining our use case","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000437,"userMentioned":false},{"id":1625000744,"metrics_meta_id":1624982568,"creationDate":1625000744,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up with marketing team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000744,"userMentioned":false},{"id":1625001002,"metrics_meta_id":1624982568,"creationDate":1625001002,"dueDate":1625325724,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Meeting with Youtube ads team","additional_info":"","comments_count":2,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625001002,"userMentioned":false},{"id":1634714355,"metrics_meta_id":1624982568,"creationDate":1634714355,"dueDate":1635359399,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"action 1","additional_info":"

hello

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634714355,"userMentioned":false},{"id":1634973847,"metrics_meta_id":1624982568,"creationDate":1634973847,"dueDate":1635704999,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"eawefawf","additional_info":"

hellwewo

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634973951,"userMentioned":false}]},{"id":1624983442,"name":"Revenue","ownerName":"Chris Harris","owner":1619367945,"currValue":35150.0,"targetValue":500000.0,"status":1,"favourite":true,"trackingPeriod":2,"pendingActionItems":[{"id":1624989233,"metrics_meta_id":1624983442,"creationDate":1624989233,"dueDate":1627560019,"author_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"owner_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"priority":0,"description":"Enable payment for venmo users","additional_info":"","comments_count":4,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624989233,"userMentioned":false},{"id":1624989326,"metrics_meta_id":1624983442,"creationDate":1624989326,"dueDate":1625140819,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Add new subscription plan","additional_info":"","comments_count":31,"watchers_count":8,"current_status":0,"unread":true,"last_updated_date":1624989326,"userMentioned":false},{"id":1624989537,"metrics_meta_id":1624983442,"creationDate":1624989537,"dueDate":1624190419,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Upsell to power users","additional_info":"","comments_count":6,"watchers_count":2,"current_status":0,"unread":true,"last_updated_date":1624989537,"userMentioned":false},{"id":1625000596,"metrics_meta_id":1624983442,"creationDate":1625000596,"dueDate":1624375324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Explore new market opportunities","additional_info":"","comments_count":5,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000596,"userMentioned":false},{"id":1634670210,"metrics_meta_id":1624983442,"creationDate":1634670210,"dueDate":1635272999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1622038576,"login":"avneesh@akto.io","name":"avneesh@akto.io"},"priority":0,"description":"Increase revenue from Latin America","additional_info":"

awfwef

","comments_count":1,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757610,"userMentioned":false},{"id":1634757813,"metrics_meta_id":1624983442,"creationDate":1634757813,"dueDate":1635272999,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":1,"description":"asd","additional_info":"

hellosadsad

","comments_count":0,"watchers_count":0,"current_status":0,"unread":true,"last_updated_date":1634757813,"userMentioned":false}]},{"id":1625041456,"name":"Price","ownerName":"Avneesh Hota","owner":1612899544,"currValue":259.0,"targetValue":11.290322,"status":0,"favourite":false,"trackingPeriod":1,"pendingActionItems":[{"id":1625046816,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046965,"metrics_meta_id":1625041456,"creationDate":1624988030,"dueDate":1625227219,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Start new FB ad campaign","additional_info":"","comments_count":3,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1624988030,"userMentioned":false},{"id":1625046975,"metrics_meta_id":1625041456,"creationDate":1624988273,"dueDate":1624622419,"author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"owner_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"},"priority":0,"description":"Explore niche online communities","additional_info":"","comments_count":12,"watchers_count":3,"current_status":0,"unread":true,"last_updated_date":1624988273,"userMentioned":false},{"id":1625046985,"metrics_meta_id":1625041456,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"last_updated_date":1625000025,"userMentioned":false},{"id":1625046996,"metrics_meta_id":1625041456,"creationDate":1625000152,"dueDate":1625239324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Follow up on Twitter mentions","additional_info":"","comments_count":1,"watchers_count":1,"current_status":1,"unread":true,"last_updated_date":1625000152,"userMentioned":false}]}],"orgAccessType":null,"teamAccessType":null,"teamId":1625221629,"usersInfoMap":{"1612899590":{"accounts":null,"emailValidated":false,"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain","passHash":null,"preferredChannel":"SLACK","salt":null,"signupInfoMap":null}}},"ip":"[0:0:0:0:0:0:0:1]","time":1634973968,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/fetchDataFromMySQL","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1625046985","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"2","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Date":"Sat, 23 Oct 2021 07:26:12 GMT","Content-Type":"text/html; charset=US-ASCII"},"method":"POST","requestPayload":{},"responsePayload":{},"ip":"[0:0:0:0:0:0:0:1]","time":1634973972,"contentType":"text/html; charset=US-ASCII","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItemActivities","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1625046985","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"2","Date":"Sat, 23 Oct 2021 07:26:12 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1625046985},"responsePayload":[],"ip":"[0:0:0:0:0:0:0:1]","time":1634973972,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/actionItem","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1625046985","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"29","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"606","Date":"Sat, 23 Oct 2021 07:26:12 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"action_item_id":1625046985},"responsePayload":{"id":1625046985,"metrics_info":{"metric_meta_id":1625041456,"name":"Price","description":"Average cost of good sold","priority":0,"value":111,"health":0},"action_item":{"id":1625046985,"creationDate":1625000025,"dueDate":1626535324,"author_info":{"id":1618123456,"login":"ankita@gmail.com","name":"Ankita Gupta"},"owner_info":{"id":1612899544,"login":"iamavneesh.hota@gmail.com","name":"Avneesh Hota"},"priority":0,"description":"Send email to conference attendees","additional_info":"","comments_count":1,"watchers_count":1,"current_status":0,"unread":true,"userMentioned":false},"watching_preference":0},"ip":"[0:0:0:0:0:0:0:1]","time":1634973975,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +, +{"path":"/api/addComment","requestHeaders":{"Origin":"http://localhost:8080","Cookie":"refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng; JSESSIONID=node062smhhsgew3frg4pp488tt362.node0","Accept":"application/json, text/plain, */*","Access-Control-Allow-Origin":"*","Connection":"keep-alive","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36","Referer":"http://localhost:8080/dashboard/actions/1625046985","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Host":"localhost:8080","access-token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Accept-Encoding":"gzip, deflate, br","Sec-Fetch-Mode":"cors","sec-ch-ua":"\"Chromium\";v=\"95\", \";Not A Brand\";v=\"99\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"Linux\"","Accept-Language":"en-GB,en-US;q=0.9,en;q=0.8","Content-Length":"90","account":"222222","Content-Type":"application/json"},"responseHeaders":{"Access-Token":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ5NzM1NDksImV4cCI6MTYzNDk3NDQ0OX0.Pnpq1AvjTfuvKvB2G82cA75AQ-G0BZfAJ6xZLSZnQUNrB6329zfPZSN1Mm9gRoH9vbXhPLfcAoeqPjGvowGc9aDdZTtRw1OJz9tHha3-C0qzuUoHkS52Soo3ina1iYND_ukDItAJPK5itvrnyeLAD1PADcaD6oK4XCzT0To_2FS16jVR8sFTHgmxcWEcAaIKgVelNkFWL_sKwUJDZfu4IfqBEYholtHP4GeBYwYA9Q2YnfjH5acUpcw1zDAbU2SkWz-O91oyMxf6_AcK9mVOcOubY15AwC-KIIpZste_lM1_2zSLQvk4UsIFw3JCpxVYQwWy07YDXvyii3_PsOEjRA","Content-Length":"327","Date":"Sat, 23 Oct 2021 07:26:46 GMT","Content-Type":"application/json;charset=utf-8"},"method":"POST","requestPayload":{"parent_id":1625046985,"comment":"

","follow_up":false,"parent_type":"action_item"},"responsePayload":{"comment":"

","edited_comments_history":[],"follow_up":false,"follow_up_done":false,"mentioned_users":[],"replies_count":0,"id":1634974007,"parent_id":1625046985,"parent_type":"action_item","timestamp":1634974007,"activity_type":"comment","author_info":{"id":1612899590,"login":"ankush@gmail.com","name":"Ankush Jain"}},"ip":"[0:0:0:0:0:0:0:1]","time":1634974007,"contentType":"application/json;charset=utf-8","cookies":[{"name":"refreshToken","value":"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcwNjA0LCJleHAiOjE2MzUwNTcwMDR9.Nta-HP6emyhgb_geG3wmrrpYc7W5daWpUvEfu9NSpYTMOAstwl0qijy-WCvPHgtDW6sJtANplcw4rX1vp7EVqrY_uS_rZAoUGd9rOsjXcTn33n1sluWthrQff-ZQ9vwLP-pxxqJQRv1gm98bZf6jBzJaNu76ec04hXJkUSvbdHc9euw60aOhZNEzDE6TeisOZd36aJnv49T6gFRBj4Mw3nSZOwrwcx3PwB8EBXFvEDXhQai3YaQD2ptHKQJG0WaVTSIX9c8C_Ob6E2grBpWw1XRJJ4x1E3z5PzzSraZlGq_ABJn3Hsv3tkY-e5jWT5VqJushk_prny9f5hsIwZpPng","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false},{"name":"JSESSIONID","value":"node062smhhsgew3frg4pp488tt362.node0","comment":null,"domain":null,"maxAge":-1,"path":null,"secure":false,"version":0,"httpOnly":false}],"statusCode":200} +] \ No newline at end of file diff --git a/apps/api-runtime/src/main/resources/version.txt b/apps/api-runtime/src/main/resources/version.txt new file mode 100644 index 0000000000..8eac1cf916 --- /dev/null +++ b/apps/api-runtime/src/main/resources/version.txt @@ -0,0 +1,2 @@ +${akto-image-tag} +${akto-build-time} diff --git a/apps/api-runtime/src/test/java/com/akto/parsers/KafkaParserTest.java b/apps/api-runtime/src/test/java/com/akto/parsers/KafkaParserTest.java new file mode 100644 index 0000000000..1079aa378e --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/parsers/KafkaParserTest.java @@ -0,0 +1,62 @@ +package com.akto.parsers; + +import com.akto.dto.HttpResponseParams; +import com.google.gson.Gson; +import org.junit.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +public class KafkaParserTest { + private final static Gson gson = new Gson(); + + @Test + public void testHappyPath() { + String message = "{\"akto_account_id\":\"1111\",\"contentType\":\"application/json;charset=utf-8\",\"ip\":\"127.0.0.1:48940\",\"method\":\"GET\",\"path\":\"/api/books\",\"requestHeaders\":\"{\\\"Accept\\\":[\\\"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\\\"],\\\"Accept-Encoding\\\":[\\\"gzip, deflate\\\"],\\\"Accept-Language\\\":[\\\"en-US,en;q=0.5\\\"],\\\"Cache-Control\\\":[\\\"no-cache\\\"],\\\"Connection\\\":[\\\"keep-alive\\\"],\\\"Cookie\\\":[\\\"G_ENABLED_IDPS=google\\\"],\\\"Pragma\\\":[\\\"no-cache\\\"],\\\"Sec-Fetch-Dest\\\":[\\\"document\\\"],\\\"Sec-Fetch-Mode\\\":[\\\"navigate\\\"],\\\"Sec-Fetch-Site\\\":[\\\"cross-site\\\"],\\\"Upgrade-Insecure-Requests\\\":[\\\"1\\\"],\\\"User-Agent\\\":[\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:94.0) Gecko/20100101 Firefox/94.0\\\"]}\",\"requestPayload\":\"\",\"responseHeaders\":\"{\\\"Content-Type\\\":[\\\"application/json;charset=utf-8\\\"]}\",\"responsePayload\":\"[{\\\"id\\\":\\\"1\\\",\\\"isbn\\\":\\\"3223\\\",\\\"title\\\":\\\"Book 1\\\",\\\"author\\\":{\\\"firstname\\\":\\\"Avneesh\\\",\\\"lastname\\\":\\\"Hota\\\"}},{\\\"id\\\":\\\"2\\\",\\\"isbn\\\":\\\"2323\\\",\\\"title\\\":\\\"Book 2\\\",\\\"author\\\":{\\\"firstname\\\":\\\"Ankush\\\",\\\"lastname\\\":\\\"Jain\\\"}}]\\n\",\"status\":\"null\",\"statusCode\":\"201\",\"time\":\"1638223603\",\"type\":\"HTTP/1.1\"}"; + HttpResponseParams httpResponseParams = null; + try { + httpResponseParams = HttpCallParser.parseKafkaMessage(message); + assertEquals("/api/books",httpResponseParams.getRequestParams().getURL()); + } catch (Exception e) { + ; + } + assertNotNull(httpResponseParams); + assertEquals(1, httpResponseParams.getHeaders().size()); + assertEquals(12, httpResponseParams.requestParams.getHeaders().size()); + + } + + @Test + public void testEmptyHeader() { + String message = "{\"akto_account_id\":\"1111\",\"contentType\":\"application/json;charset=utf-8\",\"ip\":\"127.0.0.1:48940\",\"method\":\"GET\",\"path\":\"/api/books\",\"requestHeaders\":\"{}\",\"requestPayload\":\"\",\"responseHeaders\":\"{\\\"Content-Type\\\":[\\\"application/json;charset=utf-8\\\"]}\",\"responsePayload\":\"[{\\\"id\\\":\\\"1\\\",\\\"isbn\\\":\\\"3223\\\",\\\"title\\\":\\\"Book 1\\\",\\\"author\\\":{\\\"firstname\\\":\\\"Avneesh\\\",\\\"lastname\\\":\\\"Hota\\\"}},{\\\"id\\\":\\\"2\\\",\\\"isbn\\\":\\\"2323\\\",\\\"title\\\":\\\"Book 2\\\",\\\"author\\\":{\\\"firstname\\\":\\\"Ankush\\\",\\\"lastname\\\":\\\"Jain\\\"}}]\\n\",\"status\":\"null\",\"statusCode\":\"201\",\"time\":\"1638223603\",\"type\":\"HTTP/1.1\"}"; + HttpResponseParams httpResponseParams = null; + try { + httpResponseParams = HttpCallParser.parseKafkaMessage(message); + assertEquals(0,httpResponseParams.getRequestParams().getHeaders().keySet().size()); + } catch (Exception e) { + ; + } + assertNotNull(httpResponseParams); + + } + + @Test + public void testPostQueryString() { + String message = "{\"method\":\"POST\",\"requestPayload\":\"name=a&status=available\",\"responsePayload\":\"{\\\"code\\\":200,\\\"type\\\":\\\"unknown\\\",\\\"message\\\":\\\"4\\\"}\",\"ip\":\"null\",\"type\":\"HTTP/2\",\"path\":\"https://petstore.swagger.io/v2/pet/4\",\"requestHeaders\":\"{\\\"Origin\\\":\\\"https://petstore.swagger.io\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"TE\\\":\\\"trailers\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Content-Length\\\":\\\"23\\\",\\\"Content-Type\\\":\\\"application/x-www-form-urlencoded\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"Mon, 03 Jan 2022 07:18:20 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\",\"time\":\"1641194300\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + HttpResponseParams httpResponseParams = null; + try { + httpResponseParams = HttpCallParser.parseKafkaMessage(message); + String payload = httpResponseParams.getRequestParams().getPayload(); + Map json = gson.fromJson(payload, Map.class); + + assertEquals("a", json.get("name")); + assertEquals("available", json.get("status")); + + } catch (Exception e) { + ; + } + assertNotNull(httpResponseParams); + + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/parsers/TestDBSync.java b/apps/api-runtime/src/test/java/com/akto/parsers/TestDBSync.java new file mode 100644 index 0000000000..fd7401074f --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/parsers/TestDBSync.java @@ -0,0 +1,483 @@ +package com.akto.parsers; + +import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.util.*; + +import com.akto.MongoBasedTest; +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.RuntimeFilterDao; +import com.akto.dao.SampleDataDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.AktoDataType; +import com.akto.dto.ApiCollection; +import com.akto.dto.HttpRequestParams; +import com.akto.dto.User; +import com.akto.dto.runtime_filters.RuntimeFilter; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.RequestTemplate; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLTemplate; +import com.akto.dto.type.SingleTypeInfo.ParamId; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.HttpResponseParams.Source; +import com.akto.dto.IgnoreData; +import com.akto.runtime.APICatalogSync; +import com.akto.runtime.URLAggregator; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; + +import org.bson.conversions.Bson; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class TestDBSync extends MongoBasedTest { + + private static int currAccountId = 0; + + @Before + public void changeAccountId() { + Context.accountId.set(currAccountId); + currAccountId += 1; + } + + public void testInitializer(){ + SingleTypeInfo.aktoDataTypeMap = new HashMap<>(); + SingleTypeInfo.aktoDataTypeMap.put("JWT", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("PHONE_NUMBER", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("CREDIT_CARD", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("IP_ADDRESS", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("EMAIL", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("SSN", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("UUID", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("URL", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + } + + @Test + public void testMongo() { + testInitializer(); + long u = UsersDao.instance.getMCollection().countDocuments(); + UsersDao.instance.insertOne(new User("Abc", "abc@def.gmail", new HashMap<>(), new HashMap<>())); + + // then + long v = UsersDao.instance.getMCollection().countDocuments(); + + assertEquals(u+1, v); + + } + + @Test + public void testParameterizedURL() { + testInitializer(); + String url = "link/"; + HttpResponseParams resp = TestDump2.createSampleParams("user1", url+1); + URLAggregator aggr = new URLAggregator(); + ArrayList newHeader = new ArrayList<>(); + newHeader.add("hnew"); + resp.getHeaders().put("new header", newHeader); + aggr.addURL(resp); + APICatalogSync sync = new APICatalogSync("access-token", 5); + + for (int i = 2; i <= 30; i ++ ) { + aggr.addURL(TestDump2.createSampleParams("user"+i, url+i)); + } + sync.computeDelta(aggr, true, 0); + sync.syncWithDB(false, true); + + assertEquals(0, sync.getDelta(0).getStrictURLToMethods().size()); + assertEquals(1, sync.getDelta(0).getTemplateURLToMethods().size()); + + Map.Entry entry = sync.getDelta(0).getTemplateURLToMethods().entrySet().iterator().next(); + + assertEquals(url+"INTEGER", entry.getKey().getTemplateString()); + RequestTemplate reqTemplate = entry.getValue(); + + assertEquals(30, reqTemplate.getUserIds().size()); + assertEquals(2, reqTemplate.getParameters().size()); + + RequestTemplate respTemplate = reqTemplate.getResponseTemplates().get(resp.getStatusCode()); + assertEquals(30, respTemplate.getUserIds().size()); + assertEquals(3, respTemplate.getParameters().size()); + } + + @Test + public void testImmediateSync() { + testInitializer(); + String url = "immediate/"; + + List responseParams = new ArrayList<>(); + + HttpResponseParams resp = TestDump2.createSampleParams("user1", url+1); + ArrayList newHeader = new ArrayList<>(); + newHeader.add("hnew"); + resp.getHeaders().put("new header", newHeader); + responseParams.add(resp); + + for (int i = 2; i <= 30; i ++ ) { + responseParams.add(TestDump2.createSampleParams("user"+i, url+i)); + } + + HttpCallParser parser = new HttpCallParser("access-token", 1,40,10, true); + + parser.syncFunction(responseParams, false, true); + assertTrue(parser.getSyncCount() == 0); + + parser.syncFunction(responseParams, false, true); + assertFalse(parser.getSyncCount() == 0); + + responseParams.get(0).setSource(Source.HAR); + parser.syncFunction(responseParams, false, true); + assertTrue(parser.getSyncCount() == 0); + + SampleData sd = SampleDataDao.instance.findOne(Filters.eq("_id.url", "immediate/INTEGER")); + assertEquals(10, sd.getSamples().size()); + + } + + @Test + public void testAllPaths() { + testInitializer(); + String url = "link/"; + + List responseParams = new ArrayList<>(); + + HttpResponseParams resp = TestDump2.createSampleParams("user1", url+1); + ArrayList newHeader = new ArrayList<>(); + newHeader.add("hnew"); + resp.getHeaders().put("new header", newHeader); + responseParams.add(resp); + resp.setSource(Source.HAR); + HttpCallParser parser = new HttpCallParser("access-token", 10,40,10, true); + + /* tryMergingWithKnownStrictURLs - put in delta-static */ + parser.syncFunction(responseParams, false, true); + assertTrue(parser.getSyncCount() == 0); + + /* processKnownStaticURLs */ + parser.syncFunction(responseParams, false, true); + + /* tryMergingWithKnownStrictURLs - merge with delta-static */ + responseParams.add(TestDump2.createSampleParams("user"+2, url+2)); + responseParams.add(TestDump2.createSampleParams("user"+3, url+3)); + + /* tryMergingWithKnownStrictURLs - merge with delta-template */ + responseParams.add(TestDump2.createSampleParams("user"+4, url+4)); + parser.syncFunction(responseParams, false, true); + assertTrue(parser.getSyncCount() == 0); + + /* tryMergingWithKnownTemplates */ + parser.syncFunction(responseParams, false, true); + assertTrue(parser.getSyncCount() == 0); + + /* tryMergingWithKnownStrictURLs - merge with Db url */ + url = "payment/"; + responseParams = new ArrayList<>(); + responseParams.add(TestDump2.createSampleParams("user"+2, url+2)); + responseParams.get(0).setSource(Source.HAR); + parser.syncFunction(responseParams, false, true); + responseParams = new ArrayList<>(); + responseParams.add(TestDump2.createSampleParams("user"+3, url+3)); + + /* tryMergingWithKnownStrictURLs - merge with Db url - template already exists in delta */ + responseParams.add(TestDump2.createSampleParams("user"+4, url+4)); + responseParams.get(0).setSource(Source.HAR); + parser.syncFunction(responseParams, false, true); + + } + + @Test + public void testInvalidMergeParameterizedURL() { + testInitializer(); + URLAggregator aggr = new URLAggregator(); + APICatalogSync sync = new APICatalogSync("access-token", 1); + + for (int i = 1; i <= 30; i ++ ) { + aggr.addURL(TestDump2.createSampleParams("user"+i, "payment/id"+i)); + } + sync.computeDelta(aggr, true, 123); + sync.syncWithDB(false, true); + + + assertEquals(30, sync.getDelta(123).getStrictURLToMethods().size()); + assertEquals(0, sync.getDelta(123).getTemplateURLToMethods().size()); + + HttpResponseParams resp2 = TestDump2.createSampleParams("user1", "payment/history"); + ArrayList newHeader = new ArrayList<>(); + newHeader.add("hnew"); + resp2.getHeaders().put("new header", newHeader); + URLAggregator aggr2 = new URLAggregator(); + aggr2.addURL(resp2); + + sync.computeDelta(aggr2, true, 123); + sync.syncWithDB(false, true); + + assertEquals(0, sync.getDelta(123).getStrictURLToMethods().size()); + assertEquals(1, sync.getDelta(123).getTemplateURLToMethods().size()); + + + } + + @Test + public void testInitialiseFilters() throws InterruptedException { + testInitializer(); + Context.accountId.set(10000); + int totalFilters = 2; + RuntimeFilterDao.instance.initialiseFilters(); + List runtimeFilters = RuntimeFilterDao.instance.findAll(new BasicDBObject()); + assertEquals(runtimeFilters.size(), totalFilters); + + Bson filter = Filters.eq(RuntimeFilter.NAME, RuntimeFilter.OPEN_ENDPOINTS_FILTER); + RuntimeFilterDao.instance.getMCollection().deleteOne(filter); + runtimeFilters = RuntimeFilterDao.instance.findAll(filter); + assertEquals(runtimeFilters.size(), 0); + + } + + @Test + public void testFilterHttpResponseParamsEmpty() { + HttpCallParser httpCallParser = new HttpCallParser("",0,0,0, true); + List ss = httpCallParser.filterHttpResponseParams(new ArrayList<>()); + assertEquals(ss.size(),0); + } + + @Test + public void testFilterHttpResponseParamsIpHost() { + ApiCollection.useHost = true; + HttpCallParser httpCallParser = new HttpCallParser("",0,0,0, true); + HttpResponseParams h1 = new HttpResponseParams(); + h1.requestParams = new HttpRequestParams(); + h1.requestParams.setHeaders(new HashMap<>()); + h1.requestParams.getHeaders().put("host", Collections.singletonList("127.1.2.3")); + h1.statusCode = 200; + h1.requestParams.setApiCollectionId(1000); + h1.setSource(Source.MIRRORING); + + HttpResponseParams h2 = new HttpResponseParams(); + h2.requestParams = new HttpRequestParams(); + h2.requestParams.setHeaders(new HashMap<>()); + h2.requestParams.getHeaders().put("host", Collections.singletonList("avneesh32.com")); + h2.statusCode = 200; + h2.requestParams.setApiCollectionId(1000); + h2.setSource(Source.MIRRORING); + + List ss = httpCallParser.filterHttpResponseParams(Arrays.asList(h1, h2)); + assertEquals(ss.size(),2); + assertEquals(h1.requestParams.getApiCollectionId(), 1000); + assertTrue(h2.requestParams.getApiCollectionId() != 1000); + } + + @Test + public void testFilterHttpResponseParamsWithoutHost() { + ApiCollection.useHost = false; + HttpCallParser httpCallParser = new HttpCallParser("",0,0,0, true); + + String groupName1 = "groupName1"; + int vxlanId1 = 1; + String domain1 = "domain1.com"; + + ApiCollectionsDao.instance.insertOne(new ApiCollection(vxlanId1, groupName1, 0, new HashSet<>(), null, 0)); + + HttpResponseParams h1 = new HttpResponseParams(); + h1.requestParams = new HttpRequestParams(); + h1.requestParams.setHeaders(new HashMap<>()); + h1.requestParams.getHeaders().put("host", Collections.singletonList(domain1)); + h1.statusCode = 200; + h1.requestParams.setApiCollectionId(vxlanId1); + + List filterHttpResponseParamsList = httpCallParser.filterHttpResponseParams(Collections.singletonList(h1)); + + Assertions.assertEquals(filterHttpResponseParamsList.size(),1); + Assertions.assertEquals(filterHttpResponseParamsList.get(0).requestParams.getApiCollectionId(),vxlanId1); + ApiCollection apiCollection1 = ApiCollectionsDao.instance.findOne("_id", vxlanId1); + Assertions.assertEquals(apiCollection1.getVxlanId(), vxlanId1); + Assertions.assertNull(apiCollection1.getHostName()); + + int vxlanId2 = 2; + String groupName2 = "groupName2"; + HttpResponseParams h2 = new HttpResponseParams(); + h2.requestParams = new HttpRequestParams(); + h2.statusCode = 200; + h2.requestParams.setApiCollectionId(vxlanId2); + + filterHttpResponseParamsList = httpCallParser.filterHttpResponseParams(Collections.singletonList(h2)); + + Assertions.assertEquals(filterHttpResponseParamsList.size(),1); + Assertions.assertEquals(filterHttpResponseParamsList.get(0).requestParams.getApiCollectionId(),vxlanId2); + ApiCollection apiCollection2 = ApiCollectionsDao.instance.findOne("_id", vxlanId2); + Assertions.assertEquals(apiCollection2.getVxlanId(), vxlanId2); + Assertions.assertNull(apiCollection2.getHostName()); + + int vxlanId3 = 3; + String groupName3 = "groupName3"; + HttpResponseParams h3 = new HttpResponseParams(); + h3.requestParams = new HttpRequestParams(); + h3.statusCode = 400; + h3.requestParams.setApiCollectionId(vxlanId2); + + filterHttpResponseParamsList = httpCallParser.filterHttpResponseParams(Collections.singletonList(h3)); + + Assertions.assertEquals(filterHttpResponseParamsList.size(),0); + ApiCollection apiCollection3 = ApiCollectionsDao.instance.findOne("_id", vxlanId3); + Assertions.assertNull(apiCollection3); + + + Assertions.assertEquals(httpCallParser.getHostNameToIdMap().size(), 2); + Assertions.assertNotNull(httpCallParser.getHostNameToIdMap().get("null 1")); + Assertions.assertNotNull(httpCallParser.getHostNameToIdMap().get("null 2")); + + } + + @Test + public void testFilterResponseParamsWithHost() { + ApiCollection.useHost = true; + HttpCallParser httpCallParser = new HttpCallParser("",0,0,0, true); + + String groupName1 = "groupName1"; + int vxlanId1 = 1; + String domain1 = "domain1.com"; + + ApiCollectionsDao.instance.insertOne(new ApiCollection(vxlanId1, groupName1, 0, new HashSet<>(), null, 0)); + + HttpResponseParams h1 = new HttpResponseParams(); + h1.requestParams = new HttpRequestParams(); + h1.requestParams.setHeaders(new HashMap<>()); + h1.requestParams.getHeaders().put("host", Collections.singletonList(domain1)); + h1.requestParams.setApiCollectionId(vxlanId1); + h1.statusCode = 200; + h1.setSource(Source.MIRRORING); + + httpCallParser.filterHttpResponseParams(Collections.singletonList(h1)); + + List apiCollections = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + Assertions.assertEquals(apiCollections.size(),2); + + int id = domain1.hashCode(); + ApiCollection apiCollection1 = ApiCollectionsDao.instance.findOne(Filters.eq("_id", id)); + Assertions.assertEquals(apiCollection1.getVxlanId(), 0); + Assertions.assertEquals(apiCollection1.getHostName(), domain1); + Assertions.assertEquals(httpCallParser.getHostNameToIdMap().size(),1); + Assertions.assertNotNull(httpCallParser.getHostNameToIdMap().get(domain1)); + + // *********************************** + + String groupName2 = "groupName2"; + int vxlanId2 = 2; + String domain2 = "domain2.com"; + + HttpResponseParams h2 = new HttpResponseParams(); + h2.requestParams = new HttpRequestParams(); + h2.requestParams.setHeaders(new HashMap<>()); + h2.requestParams.getHeaders().put("host", Collections.singletonList(domain2)); + h2.requestParams.setApiCollectionId(vxlanId2); + h2.statusCode = 200; + h2.setSource(Source.MIRRORING); + + httpCallParser.filterHttpResponseParams(Collections.singletonList(h2)); + + apiCollections = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + Assertions.assertEquals(apiCollections.size(),3); + + id = domain2.hashCode(); + ApiCollection apiCollection2 = ApiCollectionsDao.instance.findOne(Filters.eq("_id", id)); + Assertions.assertEquals(apiCollection2.getVxlanId(), 0); + Assertions.assertEquals(apiCollection2.getHostName(), domain2); + Assertions.assertEquals(httpCallParser.getHostNameToIdMap().size(),2); + Assertions.assertNotNull(httpCallParser.getHostNameToIdMap().get(domain2)); + + // same vxlan but different host + + String domain3 = "domain3.com"; + + HttpResponseParams h3 = new HttpResponseParams(); + h3.requestParams = new HttpRequestParams(); + h3.requestParams.setHeaders(new HashMap<>()); + h3.requestParams.getHeaders().put("host", Collections.singletonList(domain3)); + h3.requestParams.setApiCollectionId(vxlanId2); // same vxlan but different host + h3.statusCode = 200; + h3.setSource(Source.MIRRORING); + + httpCallParser.filterHttpResponseParams(Collections.singletonList(h3)); + + apiCollections = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + Assertions.assertEquals(apiCollections.size(),4); + + id = domain3.hashCode(); + ApiCollection apiCollection3 = ApiCollectionsDao.instance.findOne(Filters.eq("_id", id)); + Assertions.assertEquals(apiCollection3.getVxlanId(), 0); + Assertions.assertEquals(apiCollection3.getHostName(), domain3); + Assertions.assertEquals(httpCallParser.getHostNameToIdMap().size(),3); + Assertions.assertNotNull(httpCallParser.getHostNameToIdMap().get(domain3)); + + + // different vxlan and host but same id collision + int vxlanId4 = 4; + String groupName4 = "groupName4"; + String domain4 = "domain4.com"; + + HttpResponseParams h4 = new HttpResponseParams(); + h4.requestParams = new HttpRequestParams(); + h4.requestParams.setHeaders(new HashMap<>()); + h4.requestParams.getHeaders().put("host", Collections.singletonList(domain4)); + h4.requestParams.setApiCollectionId(vxlanId4); + h4.statusCode = 200; + h4.setSource(Source.MIRRORING); + + // before processing inserting apiCollection with same id but different vxlanId and host + int dupId = domain4.hashCode(); + ApiCollectionsDao.instance.insertOne( + new ApiCollection(dupId,"something", 0, new HashSet<>(), "hostRandom", 1234) + ); + httpCallParser.getHostNameToIdMap().put("hostRandom 1234", dupId); + + httpCallParser.filterHttpResponseParams(Collections.singletonList(h4)); + + apiCollections = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + Assertions.assertEquals(apiCollections.size(),5); + + id = domain4.hashCode(); + id += 1; // since duplicate so increased by 1 will work + ApiCollection apiCollection4 = ApiCollectionsDao.instance.findOne(Filters.eq("_id", id)); + Assertions.assertNull(apiCollection4); + } + + @Test + public void testCollisionHostNameCollection() { + ApiCollectionsDao.instance.insertOne(new ApiCollection(0, "domain", 0, new HashSet<>(), null, 0)); + HttpResponseParams h1 = new HttpResponseParams(); + h1.requestParams = new HttpRequestParams(); + h1.requestParams.setHeaders(new HashMap<>()); + h1.requestParams.getHeaders().put("host", Collections.singletonList("domain")); + h1.statusCode = 200; + h1.setSource(Source.MIRRORING); + + HttpCallParser httpCallParser = new HttpCallParser("",0,0,0, true); + httpCallParser.filterHttpResponseParams(Collections.singletonList(h1)); + + List apiCollections = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + Assertions.assertEquals(apiCollections.size(), 1); + Assertions.assertEquals(apiCollections.get(0).getId(), 0); + } + + // @Test + // public void testRemovingStaleStaticURLs() { + // ParamId staticParam = new ParamId("api/payment1", "GET", -1, false, "payment_id", SingleTypeInfo.GENERIC, 0); + // staticParam.setSubTypeString("GENERIC"); + // SingleTypeInfo staticInfo = new SingleTypeInfo(staticParam, null, new HashSet<>(), 1, 1, 1, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + // staticInfo.setSubTypeString("GENERIC"); + // ParamId templateParam = new ParamId("api/STRING", "GET", -1, false, "payment_id", SingleTypeInfo.GENERIC, 0); + // templateParam.setSubTypeString("GENERIC"); + // SingleTypeInfo templateInfo = new SingleTypeInfo(templateParam, null, new HashSet<>(), 1, 1, 1, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + // templateInfo.setSubTypeString("GENERIC"); + // SingleTypeInfoDao.instance.insertOne(staticInfo); + // SingleTypeInfoDao.instance.insertOne(templateInfo); + // HttpCallParser parser1 = new HttpCallParser("access-token", 1,40,10); + // assertEquals(parser1.apiCatalogSync.dbState.get(0).getStrictURLToMethods().size(), 0); + // parser1.syncFunction(new ArrayList<>()); + // assertEquals(SingleTypeInfoDao.instance.findAll(new BasicDBObject()).size(), 1); + + // } +} diff --git a/apps/api-runtime/src/test/java/com/akto/parsers/TestDump2.java b/apps/api-runtime/src/test/java/com/akto/parsers/TestDump2.java new file mode 100644 index 0000000000..4cc59cd8d8 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/parsers/TestDump2.java @@ -0,0 +1,328 @@ +package com.akto.parsers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.akto.dto.type.*; +import com.akto.dto.type.SingleTypeInfo.SubType; +import com.akto.dto.type.SingleTypeInfo.SuperType; +import com.akto.dto.type.URLMethods.Method; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.IgnoreData; +import com.akto.dto.AktoDataType; +import com.akto.dto.HttpRequestParams; +import com.akto.runtime.APICatalogSync; +import com.akto.runtime.URLAggregator; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; + +import org.junit.Test; + +public class TestDump2 { + + public void testInitializer(){ + SingleTypeInfo.aktoDataTypeMap = new HashMap<>(); + SingleTypeInfo.aktoDataTypeMap.put("JWT", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("PHONE_NUMBER", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("CREDIT_CARD", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("IP_ADDRESS", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("EMAIL", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("SSN", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("UUID", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("URL", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + } + public static String createSimpleResponsePayload() { + BasicDBObject ret = new BasicDBObject(); + + ret.append("a1", 1).append("b1", new BasicDBObject().append("a2", "some string").append("b2", "some number")); + + return ret.toJson(); + } + + public static String createSimpleRequestPayload() { + BasicDBObject ret = new BasicDBObject(); + + ret.append("id", 1).append("startDate", "some string"); + + return ret.toJson(); + } + + public static List createList(String s) { + List ret = new ArrayList<>(); + ret.add(s); + return ret; + } + + public static HttpResponseParams createSampleParams(String userId, String url) { + HttpResponseParams ret = new HttpResponseParams(); + ret.type = "HTTP/1.1"; + ret.statusCode = 200; + ret.status = "OK"; + ret.headers = new HashMap<>(); + ret.headers.put("Access-Token", createList(" eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ3NTc0NzIsImV4cCI6MTYzNDc1ODM3Mn0.s_UihSzOlEY9spECqSS9SaTPxygn26YodRkYZKHmNwVnVkeppT5mQYKlwiUnjpKYIHxi2a82I50c0FbJKnTTk1Z5aYcT3t8GXUar_DaaEiv3eZZcZiqeOQSnPkP_c4nhC7Fqaq4g03p4Uj_7W5qvUAkTjHOCViwE933rmfX7tZA27o-9-b_ZKXYsTLfk-FjBV7f3piHmRo88j0WpkvuQc8LwcsoUq6yPclVuDsz9YHkvM1B33_QGdZ7nGz47M33tyLXqZyeF4qsnewkOOU6vCiDnM_eqbJghbZLSqP3Ut3lfA54BlAZ-HB5gLv-2HR0m_R2thDGXXE_G_onS-ZDB6A")); + ret.headers.put("Content-Type", createList(" application/json;charset=utf-8")); + ret.headers.put("Content-Length", createList(" 762")); + ret.headers.put("Server", createList(" Jetty(9.4.42.v20210604)")); + + ret.setPayload(createSimpleResponsePayload()); + ret.requestParams = new HttpRequestParams(); + + ret.requestParams.method = "POST"; + ret.requestParams.url = url; + ret.requestParams.type = "HTTP/1.1"; + Map> headers = new HashMap<>(); + List accessTokenHeaders = new ArrayList<>(); + accessTokenHeaders.add(userId); + headers.put("access-token", accessTokenHeaders); + headers.put("Host", createList("3.7.253.154:8080")); + headers.put("Connection", createList("keep-alive")); + headers.put("Content-Length", createList("61")); + headers.put("Access-Control-Allow-Origin", createList("*")); + headers.put("Accept", createList("application/json, text/plain, */*")); + headers.put("DNT", createList("1")); + headers.put("account", createList("222222")); + headers.put("User-Agent", createList("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36")); + headers.put("Content-Type", createList("application/json")); + headers.put("Origin", createList("http://3.7.253.154:8080")); + headers.put("Referer", createList("http://3.7.253.154:8080/dashboard/boards/1624886875")); + headers.put("Accept-Encoding", createList("gzip, deflate")); + headers.put("Accept-Language", createList("en-US,en;q=0.9,mr;q=0.8")); + headers.put("Cookie", createList("JSESSIONID=node01e7k0f9f2mkm0kyan971kl7bk7.node0; refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0NzU3NDcwLCJleHAiOjE2MzQ4NDM4NzB9.MHoQpVFiYPgJrY-c4XrOlhWM20Qh1IOEKdiSne92k9p1YmUekBG7_z9osa9yYpO9Tsa1CeMs39ZDiS853boNJPAo6BcSswx6ReYHOmp3-qdu5dvqWjjQb0m-NNGGtPikvNi_d3MFmTQ0vKzu1n3WTmB_Iv-SPtmN22-Rees-VSnit6CQKvm_7kVQt-oU76LfIZ_KesfMm_vRHsFrHfKdw1zVT4XCSlPE0hJhbQNkzkwI-6zByYzG_5MnX5cyvUTIGgZ3-_VGxYRt8zPXFfAqgM1F3L4LDZSTLOu0I9gVElRP-JnSQRvYpsU0eVwP3cgS6UxxaSS_2zZU3Z_TPh8Qfg")); + + ret.requestParams.setHeaders(headers); + ret.requestParams.setPayload(createSimpleRequestPayload()); + ret.requestParams.setApiCollectionId(123); + ret.setOrig(ret.toString()); + return ret; + } + + @Test + public void testHappyPath() { + String message = " {\"akto_account_id\":\"1000000\",\"contentType\":\"application/json;charset=utf-8\",\"ip\":\"49.32.227.133:60118\",\"method\":\"GET\",\"path\":\"/api/books\",\"requestHeaders\":\"{\\\"Accept\\\":[\\\"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\\\"],\\\"Accept-Encoding\\\":[\\\"gzip, deflate\\\"],\\\"Accept-Language\\\":[\\\"en-US,en;q=0.9,mr;q=0.8\\\"],\\\"Cache-Control\\\":[\\\"no-cache\\\"],\\\"Connection\\\":[\\\"keep-alive\\\"],\\\"Dnt\\\":[\\\"1\\\"],\\\"Pragma\\\":[\\\"no-cache\\\"],\\\"Upgrade-Insecure-Requests\\\":[\\\"1\\\"],\\\"User-Agent\\\":[\\\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36\\\"]}\",\"requestPayload\":\"\",\"responseHeaders\":\"{\\\"Content-Type\\\":[\\\"application/json;charset=utf-8\\\"]}\",\"responsePayload\":\"{\\\"id\\\":\\\"1\\\",\\\"isbn\\\":\\\"3223\\\",\\\"title\\\":\\\"Book 1\\\",\\\"author\\\":{\\\"firstname\\\":\\\"Avneesh\\\",\\\"lastname\\\":\\\"Hota\\\"}}\\n\",\"status\":\"null\",\"statusCode\":\"201\",\"time\":\"1638940067\",\"type\":\"HTTP/1.1\"}"; + HttpResponseParams httpResponseParams = null; + try { + httpResponseParams = HttpCallParser.parseKafkaMessage(message); + } catch (Exception e) { + assertEquals(2, 1); + return; + } + + URLAggregator aggr = new URLAggregator(); + APICatalogSync sync = new APICatalogSync("access-token", 5); + + aggr.addURL(httpResponseParams); + sync.computeDelta(aggr, false, 0); + APICatalogSync.DbUpdateReturn dbUpdateReturn = sync.getDBUpdatesForParams(sync.getDelta(0), sync.getDbState(0), false); + assertEquals(15, dbUpdateReturn.bulkUpdatesForSingleTypeInfo.size()); + assertEquals(2, sync.getDBUpdatesForTraffic(0, sync.getDelta(0)).size()); + assertEquals(1, sync.getDBUpdatesForSampleData(0, sync.getDelta(0), sync.getDbState(0),false,true).size()); + } + + + public void simpleTestForSingleCollection(int collectionId, APICatalogSync sync) { + { + String url = "https://someapi.com/link1"; + HttpResponseParams resp = createSampleParams("user1", url); + + URLAggregator aggr = new URLAggregator(); + + aggr.addURL(resp); + sync.computeDelta(aggr, false, collectionId); + + Map urlMethodsMap = sync.getDelta(collectionId).getStrictURLToMethods(); + assertEquals(1, urlMethodsMap.size()); + + Method method = Method.fromString(resp.getRequestParams().getMethod()); + RequestTemplate reqTemplate = urlMethodsMap.get(new URLStatic(resp.getRequestParams().getURL(), method)); + + assertEquals(1, reqTemplate.getUserIds().size()); + assertEquals(2, reqTemplate.getParameters().size()); + + RequestTemplate respTemplate = reqTemplate.getResponseTemplates().get(resp.statusCode); + assertEquals(1, respTemplate.getUserIds().size()); + assertEquals(3, respTemplate.getParameters().size()); + APICatalogSync.DbUpdateReturn dbUpdateReturn = sync.getDBUpdatesForParams(sync.getDelta(collectionId), sync.getDbState(collectionId), false); + assertEquals(24, dbUpdateReturn.bulkUpdatesForSingleTypeInfo.size()); + assertEquals(2, sync.getDBUpdatesForTraffic(collectionId, sync.getDelta(collectionId)).size()); + } + } + + @Test + public void simpleTest() { + testInitializer(); + APICatalogSync sync = new APICatalogSync("access-token", 5); + simpleTestForSingleCollection(0, sync); + simpleTestForSingleCollection(1, sync); + simpleTestForSingleCollection(2, sync); + assertEquals(24, sync.getDBUpdatesForParams(sync.getDelta(0), sync.getDbState(0),false).bulkUpdatesForSingleTypeInfo.size()); + assertEquals(24, sync.getDBUpdatesForParams(sync.getDelta(1), sync.getDbState(1),false).bulkUpdatesForSingleTypeInfo.size()); + assertEquals(24, sync.getDBUpdatesForParams(sync.getDelta(2), sync.getDbState(2),false).bulkUpdatesForSingleTypeInfo.size()); + } + + @Test + public void getParamsTest() { + String baseurl = "https://someapi.com/example"; + String url = baseurl+"?p1=v1&p2=v2&p3=%7B%22a%22%3A1%2C%22b%22%3A%5B%7B%22c%22%3A1%2C%22d%22%3A1%7D%5D%7D"; + HttpResponseParams resp = createSampleParams("user1", url); + + URLAggregator aggr = new URLAggregator(); + APICatalogSync sync = new APICatalogSync("access-token", 5); + + aggr.addURL(resp); + sync.computeDelta(aggr, false, 0); + + Map urlMethodsMap = sync.getDelta(0).getStrictURLToMethods(); + assertEquals(1, urlMethodsMap.size()); + + assertEquals(baseurl, urlMethodsMap.keySet().iterator().next().getUrl()); + RequestTemplate reqTemplate = urlMethodsMap.get(new URLStatic(baseurl, Method.fromString(resp.getRequestParams().method))); + assertEquals(1, reqTemplate.getUserIds().size()); + assertEquals(5, reqTemplate.getParameters().size()); + + assertEquals(2, sync.getDBUpdatesForTraffic(0, sync.getDelta(0)).size()); + } + + + @Test + public void urlsTest() { + Set responses = new HashSet<>(); + String url = "https://someapi.com/link1"; + + HttpResponseParams resp = createSampleParams("user1", url); + responses.add(resp); + responses.add(createSampleParams("user2", url)); + responses.add(createSampleParams("user3", url)); + responses.add(createSampleParams("user4", url)); + responses.add(createSampleParams("user5", url)); + + URLAggregator aggr = new URLAggregator(); + APICatalogSync sync = new APICatalogSync("access-token", 5); + Method method = Method.fromString(resp.getRequestParams().getMethod()); + aggr.addURL(responses, new URLStatic(resp.getRequestParams().getURL(), method)); + sync.computeDelta(aggr, false, 0); + + Map urlMethodsMap = sync.getDelta(0).getStrictURLToMethods(); + assertEquals(1, urlMethodsMap.size()); + + RequestTemplate reqTemplate = urlMethodsMap.get(new URLStatic(resp.getRequestParams().getURL(), method)); + assertEquals(5, reqTemplate.getUserIds().size()); + assertEquals(2, reqTemplate.getParameters().size()); + + RequestTemplate respTemplate = reqTemplate.getResponseTemplates().get(resp.statusCode); + assertEquals(5, respTemplate.getUserIds().size()); + assertEquals(3, respTemplate.getParameters().size()); + assertEquals(2, sync.getDBUpdatesForTraffic(0, sync.getDelta(0)).size()); + } + + @Test + public void testParameterizedURLsTest() { + String url = "link/"; + HttpResponseParams resp = createSampleParams("user1", url+1); + URLAggregator aggr = new URLAggregator(); + resp.requestParams.getHeaders().put("newHeader", new ArrayList()); + aggr.addURL(resp); + APICatalogSync sync = new APICatalogSync("access-token", 1); + + for (int i = 2; i <= 30; i ++ ) { + aggr.addURL(createSampleParams("user"+i, url+i)); + } + + sync.computeDelta(aggr, true, 0); + + Map urlTemplateMap = sync.getDelta(0).getTemplateURLToMethods(); + + assertEquals(1, urlTemplateMap.size()); + + Map.Entry entry = urlTemplateMap.entrySet().iterator().next(); + assertEquals(url+"INTEGER", entry.getKey().getTemplateString()); + + RequestTemplate reqTemplate = entry.getValue(); + + assertEquals(30, reqTemplate.getUserIds().size()); + assertEquals(2, reqTemplate.getParameters().size()); + + RequestTemplate respTemplate = reqTemplate.getResponseTemplates().get(resp.statusCode); + assertEquals(30, respTemplate.getUserIds().size()); + assertEquals(3, respTemplate.getParameters().size()); + assertEquals(2, sync.getDBUpdatesForTraffic(0, sync.getDelta(0)).size()); + } + + private String createPayloadWithRepetitiveKeys(String i) { + BasicDBObject ret = new BasicDBObject(); + BasicDBList list = new BasicDBList(); + + ret.put("a", list); + + list.add(new BasicDBObject("i"+i, i)); + + return ret.toJson(); + } + + @Test + public void repetitiveKeyTest() { + String url = "https://someapi.com/link1"; + + Set responseParams = new HashSet<>(); + + HttpResponseParams resp = createSampleParams("user1", url); + resp.setPayload(createPayloadWithRepetitiveKeys("1")); + responseParams.add(resp); + + for (int i = 2 ; i < 30; i ++) { + resp = createSampleParams("user"+i, url); + resp.setPayload(createPayloadWithRepetitiveKeys(""+i)); + responseParams.add(resp); + } + + Method method = Method.fromString(resp.getRequestParams().getMethod()); + + URLAggregator aggr = new URLAggregator(); + APICatalogSync sync = new APICatalogSync("access-token", 5); + + aggr.addURL(responseParams, new URLStatic(url, method)); + sync.computeDelta(aggr, false, 0); + + Map urlMethodsMap = sync.getDelta(0).getStrictURLToMethods(); + assertEquals(1, urlMethodsMap.size()); + + RequestTemplate reqTemplate = urlMethodsMap.get(new URLStatic(resp.getRequestParams().getURL(), method)); + assertEquals(10, reqTemplate.getUserIds().size()); + assertEquals(2, reqTemplate.getParameters().size()); + + RequestTemplate respTemplate = reqTemplate.getResponseTemplates().get(resp.statusCode); + assertEquals(10, respTemplate.getUserIds().size()); + assertEquals(29, respTemplate.getParameters().size()); + + respTemplate.tryMergeNodesInTrie(url, "POST", resp.statusCode, resp.getRequestParams().getApiCollectionId()); + + List updates = sync.getDBUpdatesForParams(sync.getDelta(0), sync.getDbState(0), false).bulkUpdatesForSingleTypeInfo; + } + + @Test + public void testURLMatch() { + String url = "https://amazonpay.amazon.in/ap/signin?openid.return_to=https%3A%2F%2Famazonpay.amazon.in%2Fv1%2Finitiate-payment%3FredirectUrl%3Dhttps%253A%252F%252Fsandbox.juspay.in%252Fv2%252Fpay%252Fresponse-amazonpay%252Fmpl_qa%26payload%3DqpZtIG4rQa0Ru6HR1RSOlDtxA61%252BVNb0WLLwzMgLnsStcLU9nD%252FbQ2XZLKvWNqdViQ5YZujSRCPagD%252FVME0JWyl3fhlh1s69%252FCaKfQiDnTg42Ofgqxj5CN86Mv45MhbmzJFVZ0JRM1yECFrLkdLnGJOr4c%252FZQoWJ3CeRGl3XcYF807JC%252F0iidvC62N3qQm97ketMo9af%252FQjTL4NTOkzPVwv1bNeI%252F8Ea5uQxWtBdZATV6ogzHgFMeM4tzcbJY5E0XxeTjhJ1SijDXLtgSOoERFCPxLzudyb9%252B2IoF9cxNWb8yi9RJuqn%252BMvU4BC%252FFrgJaLn9DJ9r4RE%253D%26iv%3DE2XGT7As7Kdo50sj%26key%3DFoXRG7XfML%252B9UAO88iH7hfSyNfNbhgdPT7d3%252F8G%252B9sqovuZOct4ZNf88yR%252FtgbRedAsVG%252BZJHjeOHlKlFZoomrm2IWweysOvMQrDyIL35hT2NUoG4ZCG94ZFC2b7TII4XEFId%252Bkpj0qMUreKQafh0NXu2jg58ogzAWgpU5uskZBUg3WDITJMQXdGqaOPO6gooIEtKmLV6gQx4%252F%252B9K18XKofG2fZQ5bNlvpuFbyn4%252Brs3J%252BtJxPsxnuSiPrJwGEk36rDjhW1LOgssrAAUv%252BSfExHQ3KfmFnBdbK2rWM0CkwgYZ95cteVxRDl7f7SdpgBCmrlVVcPvM2moUiWOTW9aHA%253D%253D&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.assoc_handle=amazon_pay_in_mobile&openid.mode=checkid_setup&marketPlaceId=A3FDG49KKM823Y&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&pageId=amzn_pay_in&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.pape.max_auth_age=5400&siteState=clientContext%3D257-1861896-6931844%2CsourceUrl%3Dhttps%253A%252F%252Famazonpay.amazon.in%252Fv1%252Finitiate-payment%253FredirectUrl%253Dhttps%25253A%25252F%25252Fsandbox.juspay.in%25252Fv2%25252Fpay%25252Fresponse-amazonpay%25252Fmpl_qa%2526payload%253DqpZtIG4rQa0Ru6HR1RSOlDtxA61%25252BVNb0WLLwzMgLnsStcLU9nD%25252FbQ2XZLKvWNqdViQ5YZujSRCPagD%25252FVME0JWyl3fhlh1s69%25252FCaKfQiDnTg42Ofgqxj5CN86Mv45MhbmzJFVZ0JRM1yECFrLkdLnGJOr4c%25252FZQoWJ3CeRGl3XcYF807JC%25252F0iidvC62N3qQm97ketMo9af%25252FQjTL4NTOkzPVwv1bNeI%25252F8Ea5uQxWtBdZATV6ogzHgFMeM4tzcbJY5E0XxeTjhJ1SijDXLtgSOoERFCPxLzudyb9%25252B2IoF9cxNWb8yi9RJuqn%25252BMvU4BC%25252FFrgJaLn9DJ9r4RE%25253D%2526iv%253DE2XGT7As7Kdo50sj%2526key%253DFoXRG7XfML%25252B9UAO88iH7hfSyNfNbhgdPT7d3%25252F8G%25252B9sqovuZOct4ZNf88yR%25252FtgbRedAsVG%25252BZJHjeOHlKlFZoomrm2IWweysOvMQrDyIL35hT2NUoG4ZCG94ZFC2b7TII4XEFId%25252Bkpj0qMUreKQafh0NXu2jg58ogzAWgpU5uskZBUg3WDITJMQXdGqaOPO6gooIEtKmLV6gQx4%25252F%25252B9K18XKofG2fZQ5bNlvpuFbyn4%25252Brs3J%25252BtJxPsxnuSiPrJwGEk36rDjhW1LOgssrAAUv%25252BSfExHQ3KfmFnBdbK2rWM0CkwgYZ95cteVxRDl7f7SdpgBCmrlVVcPvM2moUiWOTW9aHA%25253D%25253D%2Csignature%3Dj2BY7ki63y4rphlJZ6WQZhGj2F5fMyEj3D"; + assertTrue(KeyTypes.patternToSubType.get(SingleTypeInfo.URL).matcher(url).matches()); + } + + @Test + public void test2() { + String[] urlTokens = APICatalogSync.tokenize("https://qapi.mpl.live:443/{param_STRING}/pending-invites"); + urlTokens[3] = null; + SuperType[] types = new SuperType[urlTokens.length]; + types[3] = SuperType.STRING; + URLTemplate urlTemplate = new URLTemplate(urlTokens, types, Method.POST); + assertFalse(urlTemplate.match("https://qapi.mpl.live:443/kyc/for-payments", Method.POST)); + + assertTrue(urlTemplate.match("https://qapi.mpl.live:443/12312/pending-invites", Method.POST)); + assertFalse(urlTemplate.match("https://qapi.mpl.live:443/12312/sdfdasfa", Method.POST)); + assertFalse(urlTemplate.match("https://qapi.mpl.live:443/abc/pending-invites", Method.GET)); + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/parsers/TestMergingNew.java b/apps/api-runtime/src/test/java/com/akto/parsers/TestMergingNew.java new file mode 100644 index 0000000000..8d36202913 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/parsers/TestMergingNew.java @@ -0,0 +1,800 @@ +package com.akto.parsers; + +import com.akto.MongoBasedTest; +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.SampleDataDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.context.Context; +import com.akto.dto.AktoDataType; +import com.akto.dto.HttpRequestParams; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.IgnoreData; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.*; +import com.akto.runtime.APICatalogSync; +import com.akto.utils.RedactSampleData; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import org.bson.conversions.Bson; +import org.junit.Test; + +import java.util.*; + +import static com.akto.parsers.TestDump2.createList; +import static org.junit.Assert.*; + +public class TestMergingNew extends MongoBasedTest { + + public void testInitializer(){ + SingleTypeInfo.aktoDataTypeMap = new HashMap<>(); + SingleTypeInfo.aktoDataTypeMap.put("JWT", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("PHONE_NUMBER", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("CREDIT_CARD", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("IP_ADDRESS", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("EMAIL", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("SSN", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("UUID", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("URL", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + } + @Test + public void testMultipleIntegerMerging() { + testInitializer(); + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + String url = "api/"; + List responseParams = new ArrayList<>(); + List urls = new ArrayList<>(); + for (int i=0; i< 50; i++) { + urls.add(url + i + "/books/" + (i+1) + "/cars/" + (i+3)); + } + for (String c: urls) { + HttpResponseParams resp = createSampleParams("user1", c); + responseParams.add(resp); + } + + parser.syncFunction(responseParams.subList(0,10), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + parser.syncFunction(responseParams.subList(10,15), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + assertEquals(0, getStaticURLsSize(parser)); + + parser.syncFunction(responseParams.subList(15,20), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + assertEquals(0, getStaticURLsSize(parser)); + + + + Map urlTemplateMap = parser.apiCatalogSync.getDelta(123).getTemplateURLToMethods(); + + + assertEquals(1, urlTemplateMap.size()); + assertEquals(0, getStaticURLsSize(parser)); + + } + + public int getStaticURLsSize(HttpCallParser parser) { + Map urlStaticMap = parser.apiCatalogSync.getDelta(123).getStrictURLToMethods(); + + return urlStaticMap.size(); + } + + @Test + public void testmultipleUUIDForceMerge(){ + testInitializer(); + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1,true); + String url = "api/product/"; + List responseParams = new ArrayList<>(); + List urls = new ArrayList<>(); + while (urls.size() < 25) { + UUID uuid = UUID.randomUUID(); + UUID uuid2 = UUID.randomUUID(); + UUID uuid3 = UUID.randomUUID(); + String finalUrl = url + uuid + "/subproduct/" + uuid2 + "/subitem/" + uuid3 + "/id/" + urls.size(); + urls.add(finalUrl); + } + + int i = 0; + for (String c: urls) { + HttpResponseParams resp = createDifferentHttpResponseParams(i*100, c); + responseParams.add(resp); + i +=1; + } + + parser.syncFunction(responseParams.subList(0,1), false,true); + parser.apiCatalogSync.syncWithDB(false,true); + assertEquals(1, getStaticURLsSize(parser)); + + parser.syncFunction(responseParams.subList(1,2), false,true); + parser.apiCatalogSync.syncWithDB(false,true); + assertEquals(0, getStaticURLsSize(parser)); + + Map templateURLToMethods = parser.apiCatalogSync.getDbState(123).getTemplateURLToMethods(); + assertEquals(1, templateURLToMethods.size()); + + parser.syncFunction(responseParams.subList(3,10), false,true); + parser.syncFunction(Collections.singletonList(createDifferentHttpResponseParams(10000, + url + "avneesh@akto.io" + "/subproduct/" + "avneesh@akto.io" + "/subitem/" + "avneesh@akto.io" + "/id/" + "112" + )), false,true); // adding this just to see if multiple subTypes of urlParams are recorded or not (not for UUID merging) + parser.apiCatalogSync.syncWithDB(false,true); + assertEquals(0, getStaticURLsSize(parser)); + + templateURLToMethods = parser.apiCatalogSync.getDbState(123).getTemplateURLToMethods(); + URLTemplate urlTemplate = APICatalogSync.createUrlTemplate( + url + "STRING" + "/subproduct/" + "STRING" + "/subitem/" + "STRING" + "/id/" + "INTEGER" + , URLMethods.Method.GET); + RequestTemplate requestTemplate = templateURLToMethods.get(urlTemplate); + Map keyTypesMap = requestTemplate.getUrlParams(); + KeyTypes keyTypes = keyTypesMap.get(2); + + assertEquals(2, keyTypes.getOccurrences().size()); + SingleTypeInfo singleTypeInfo1 = keyTypes.getOccurrences().get(SingleTypeInfo.UUID); + assertNotNull(singleTypeInfo1); + SingleTypeInfo singleTypeInfo2 = keyTypes.getOccurrences().get(SingleTypeInfo.EMAIL); + assertNotNull(singleTypeInfo2); + } + + @Test + public void testUUIDForceMerge() { + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + String url = "api/notifications/"; + List responseParams = new ArrayList<>(); + List urls = new ArrayList<>(); + while (urls.size() < 50) { + UUID uuid = UUID.randomUUID(); + String finalUrl = url + uuid + "/received"; + urls.add(finalUrl); + } + + int i = 0; + for (String c: urls) { + HttpResponseParams resp = createDifferentHttpResponseParams(i*100, c); + responseParams.add(resp); + i +=1; + } + + parser.syncFunction(responseParams.subList(0,1), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + assertEquals(1, getStaticURLsSize(parser)); + + parser.syncFunction(responseParams.subList(1,2), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + assertEquals(0, getStaticURLsSize(parser)); + + Map templateURLToMethods = parser.apiCatalogSync.getDbState(123).getTemplateURLToMethods(); + assertEquals(1, templateURLToMethods.size()); + + parser.syncFunction(responseParams.subList(3,10), false, true); + parser.syncFunction(Collections.singletonList(createDifferentHttpResponseParams(10000, url+"avneesh@akto.io"+"/received")), false, true); // adding this just to see if multiple subTypes of urlParams are recorded or not (not for UUID merging) + parser.apiCatalogSync.syncWithDB(false, true); + assertEquals(0, getStaticURLsSize(parser)); + + templateURLToMethods = parser.apiCatalogSync.getDbState(123).getTemplateURLToMethods(); + URLTemplate urlTemplate = APICatalogSync.createUrlTemplate(url+"STRING"+"/received", URLMethods.Method.GET); + RequestTemplate requestTemplate = templateURLToMethods.get(urlTemplate); + Map keyTypesMap = requestTemplate.getUrlParams(); + KeyTypes keyTypes = keyTypesMap.get(2); + + assertEquals(2, keyTypes.getOccurrences().size()); + SingleTypeInfo singleTypeInfo1 = keyTypes.getOccurrences().get(SingleTypeInfo.UUID); + assertNotNull(singleTypeInfo1); + SingleTypeInfo singleTypeInfo2 = keyTypes.getOccurrences().get(SingleTypeInfo.EMAIL); + assertNotNull(singleTypeInfo2); + } + + @Test + public void testParameterizedURLsTestString() { + testInitializer(); + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + String url = "link/"; + List responseParams = new ArrayList<>(); + List urls = new ArrayList<>(); + for (String x: Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H")) { + for (int i=0; i< 50; i++) { + urls.add(url+x+i); + } + } + for (String c: urls) { + HttpResponseParams resp = createSampleParams("user1", c); + responseParams.add(resp); + } + + parser.syncFunction(responseParams.subList(0,23), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + assertEquals(23, getStaticURLsSize(parser)); + + parser.syncFunction(responseParams.subList(23,28), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + assertEquals(0, getStaticURLsSize(parser)); + + parser.syncFunction(responseParams.subList(28,33), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + assertEquals(0, getStaticURLsSize(parser)); + } + + @Test + public void testNonJsonResponsePayloadPayload() throws Exception { + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + + String a = "{\"path\": \"https://invoices.razorpay.com/v1/l/inv_\", \"method\": \"POST\", \"type\": \"HTTP/1.1\", \"requestHeaders\": \"{\\\"X-Killbill-ApiKey\\\": \\\"mplgaming\\\", \\\"Authorization\\\": \\\"Basic somerandom=\\\", \\\"X-Killbill-ApiSecret\\\": \\\"something\\\", \\\"Accept\\\": \\\"application/json\\\", \\\"X-MPL-COUNTRYCODE\\\": \\\"IN\\\", \\\"X-Killbill-CreatedBy\\\": \\\"test-payment\\\", \\\"Content-type\\\": \\\"application/json\\\"}\", \"requestPayload\": \"{}\", \"statusCode\": \"200\", \"responseHeaders\": \"{\\\"Date\\\": \\\"Mon, 18 Apr 2022 13:05:16 GMT\\\", \\\"Content-Type\\\": \\\"application/json\\\", \\\"Transfer-Encoding\\\": \\\"chunked\\\", \\\"Connection\\\": \\\"keep-alive\\\", \\\"Server\\\": \\\"Apache-Coyote/1.1\\\", \\\"Access-Control-Allow-Origin\\\": \\\"*\\\", \\\"Access-Control-Allow-Methods\\\": \\\"GET, POST, DELETE, PUT, OPTIONS\\\", \\\"Access-Control-Allow-Headers\\\": \\\"Authorization,Content-Type,Location,X-Killbill-ApiKey,X-Killbill-ApiSecret,X-Killbill-Comment,X-Killbill-CreatedBy,X-Killbill-Pagination-CurrentOffset,X-Killbill-Pagination-MaxNbRecords,X-Killbill-Pagination-NextOffset,X-Killbill-Pagination-NextPageUri,X-Killbill-Pagination-TotalNbRecords,X-Killbill-Reason\\\", \\\"Access-Control-Expose-Headers\\\": \\\"Authorization,Content-Type,Location,X-Killbill-ApiKey,X-Killbill-ApiSecret,X-Killbill-Comment,X-Killbill-CreatedBy,X-Killbill-Pagination-CurrentOffset,X-Killbill-Pagination-MaxNbRecords,X-Killbill-Pagination-NextOffset,X-Killbill-Pagination-NextPageUri,X-Killbill-Pagination-TotalNbRecords,X-Killbill-Reason\\\", \\\"Access-Control-Allow-Credentials\\\": \\\"true\\\"}\", \"status\": \"OK\", \"responsePayload\": \"aaaaa\", \"ip\": \"\", \"time\": \"1650287116\", \"akto_account_id\": \"1000000\", \"akto_vxlan_id\": 123, \"source\": \"OTHER\"}"; + List responseParams = new ArrayList<>(); + for (int i = 0; i < 30; i++) { + HttpResponseParams httpResponseParams = HttpCallParser.parseKafkaMessage(a); + httpResponseParams.requestParams.url += i; + responseParams.add(httpResponseParams); + } + + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + + parser.syncFunction(responseParams.subList(0,10), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + + parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + + parser.syncFunction(responseParams.subList(10,25), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + parser.syncFunction(responseParams.subList(25,30), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + + + Map urlTemplateMap = parser.apiCatalogSync.getDelta(0).getTemplateURLToMethods(); + Map urlStaticMap = parser.apiCatalogSync.getDelta(0).getStrictURLToMethods(); + + assertEquals(urlTemplateMap.size(), 1); + assertEquals(urlStaticMap.size(), 0); + + } + + + + + @Test + public void testEmptyResponsePayload() throws Exception { + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + + String a = "{\"path\": \"http://killdill.mpl.internal:8080/1.0/kb/paymentGateways/hosted/form/bbb-bbbb-bbb?paymentMethodId=qq-qqq-qqq\", \"method\": \"POST\", \"type\": \"HTTP/1.1\", \"requestHeaders\": \"{\\\"X-Killbill-ApiKey\\\": \\\"mplgaming\\\", \\\"Authorization\\\": \\\"Basic somerandom=\\\", \\\"X-Killbill-ApiSecret\\\": \\\"something\\\", \\\"Accept\\\": \\\"application/json\\\", \\\"X-MPL-COUNTRYCODE\\\": \\\"IN\\\", \\\"X-Killbill-CreatedBy\\\": \\\"test-payment\\\", \\\"Content-type\\\": \\\"application/json\\\"}\", \"requestPayload\": \"{\\\"formFields\\\":[{\\\"key\\\":\\\"amount\\\",\\\"value\\\":\\\"125.000\\\"},{\\\"key\\\":\\\"netAmount\\\",\\\"value\\\":\\\"125.000\\\"},{\\\"key\\\":\\\"currency\\\",\\\"value\\\":\\\"INR\\\"},{\\\"key\\\":\\\"orderId\\\",\\\"value\\\":\\\"ASGARD\\\"},{\\\"key\\\":\\\"paymentMethodId\\\",\\\"value\\\":\\\"zzzz-zzz-zzz-zzzz-zzzz\\\"},{\\\"key\\\":\\\"mobileNumber\\\",\\\"value\\\":\\\"+917021916328\\\"},{\\\"key\\\":\\\"countryCode\\\",\\\"value\\\":\\\"IN\\\"},{\\\"key\\\":\\\"chargeDetails\\\",\\\"value\\\":\\\"{\\\\\\\"charges\\\\\\\":[],\\\\\\\"totalCharges\\\\\\\":0,\\\\\\\"totalChargesLC\\\\\\\":0}\\\"},{\\\"key\\\":\\\"pegRate\\\",\\\"value\\\":\\\"1.0000\\\"},{\\\"key\\\":\\\"extraInfo\\\",\\\"value\\\":\\\"{\\\\\\\"paymentMode\\\\\\\":\\\\\\\"NB_ICICI\\\\\\\",\\\\\\\"additionalPluginInfo\\\\\\\":\\\\\\\"{\\\\\\\\\\\\\\\"merchantId\\\\\\\\\\\\\\\":\\\\\\\\\\\\\\\"mpl_qa\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"clientId\\\\\\\\\\\\\\\":\\\\\\\\\\\\\\\"mplgaming\\\\\\\\\\\\\\\"}\\\\\\\",\\\\\\\"paymentModePluginInfo\\\\\\\":\\\\\\\"{\\\\\\\\\\\\\\\"code\\\\\\\\\\\\\\\":\\\\\\\\\\\\\\\"NB_ICICI\\\\\\\\\\\\\\\"}\\\\\\\",\\\\\\\"paymentMethodType\\\\\\\":\\\\\\\"netbanking\\\\\\\",\\\\\\\"paymentFlow\\\\\\\":\\\\\\\"JP2_AT_R4\\\\\\\"}\\\"},{\\\"key\\\":\\\"appVersion\\\",\\\"value\\\":\\\"1000174\\\"},{\\\"key\\\":\\\"savedPaymentDetails\\\",\\\"value\\\":\\\"{}\\\"},{\\\"key\\\":\\\"appType\\\",\\\"value\\\":\\\"CASH\\\"},{\\\"key\\\":\\\"savedPaymentDetails\\\",\\\"value\\\":\\\"{}\\\"}]}\", \"statusCode\": \"200\", \"responseHeaders\": \"{\\\"Date\\\": \\\"Mon, 18 Apr 2022 13:05:16 GMT\\\", \\\"Content-Type\\\": \\\"application/json\\\", \\\"Transfer-Encoding\\\": \\\"chunked\\\", \\\"Connection\\\": \\\"keep-alive\\\", \\\"Server\\\": \\\"Apache-Coyote/1.1\\\", \\\"Access-Control-Allow-Origin\\\": \\\"*\\\", \\\"Access-Control-Allow-Methods\\\": \\\"GET, POST, DELETE, PUT, OPTIONS\\\", \\\"Access-Control-Allow-Headers\\\": \\\"Authorization,Content-Type,Location,X-Killbill-ApiKey,X-Killbill-ApiSecret,X-Killbill-Comment,X-Killbill-CreatedBy,X-Killbill-Pagination-CurrentOffset,X-Killbill-Pagination-MaxNbRecords,X-Killbill-Pagination-NextOffset,X-Killbill-Pagination-NextPageUri,X-Killbill-Pagination-TotalNbRecords,X-Killbill-Reason\\\", \\\"Access-Control-Expose-Headers\\\": \\\"Authorization,Content-Type,Location,X-Killbill-ApiKey,X-Killbill-ApiSecret,X-Killbill-Comment,X-Killbill-CreatedBy,X-Killbill-Pagination-CurrentOffset,X-Killbill-Pagination-MaxNbRecords,X-Killbill-Pagination-NextOffset,X-Killbill-Pagination-NextPageUri,X-Killbill-Pagination-TotalNbRecords,X-Killbill-Reason\\\", \\\"Access-Control-Allow-Credentials\\\": \\\"true\\\"}\", \"status\": \"OK\", \"responsePayload\": \"\", \"ip\": \"\", \"time\": \"1650287116\", \"akto_account_id\": \"1000000\", \"akto_vxlan_id\": 123, \"source\": \"OTHER\"}"; + List responseParams = new ArrayList<>(); + for (int i = 0; i < 30; i++) { + HttpResponseParams httpResponseParams = HttpCallParser.parseKafkaMessage(a); + String[] s = httpResponseParams.requestParams.url.split("/"); + s[s.length-1] = "param"+i; + httpResponseParams.requestParams.url = String.join("/", s) + "?paymentMethodId=qq-qqq-qqq"; + responseParams.add(httpResponseParams); + } + + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + + parser.syncFunction(responseParams.subList(0,10), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + parser.syncFunction(responseParams.subList(10,25), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + parser.syncFunction(responseParams.subList(25,30), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + + + Map urlTemplateMap = parser.apiCatalogSync.getDelta(0).getTemplateURLToMethods(); + Map urlStaticMap = parser.apiCatalogSync.getDelta(0).getStrictURLToMethods(); + + assertEquals(urlTemplateMap.size(), 1); + assertEquals(urlStaticMap.size(), 0); + + } + + @Test + public void testStrictIntoTemplate() { + testInitializer(); + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + String url = "api/books/"; + List responseParams = new ArrayList<>(); + List urls = new ArrayList<>(); + for (int i=0; i< 50; i++) { + urls.add(url + "c"+i); + } + for (String c: urls) { + HttpResponseParams resp = createSampleParams("user1", c); + responseParams.add(resp); + } + + parser.apiCatalogSync.dbState = new HashMap<>(); + parser.apiCatalogSync.dbState.put(123, new APICatalog(0, new HashMap<>(), new HashMap<>())); + parser.apiCatalogSync.getDbState(123).setTemplateURLToMethods(new HashMap<>()); + URLTemplate urlTemplate = APICatalogSync.tryMergeUrls(new URLStatic(responseParams.get(0).requestParams.url, URLMethods.Method.GET), new URLStatic(responseParams.get(1).requestParams.url, URLMethods.Method.GET)); + parser.apiCatalogSync.getDbState(123).getTemplateURLToMethods().put(urlTemplate, new RequestTemplate(new HashMap<>(), new HashMap<>(), new HashMap<>(), new TrafficRecorder(new HashMap<>()))); + + parser.syncFunction(responseParams.subList(0,15), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + parser.syncFunction(responseParams.subList(15,25), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + parser.syncFunction(responseParams.subList(25,30), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + + + Map urlTemplateMap = parser.apiCatalogSync.getDelta(123).getTemplateURLToMethods(); + Map urlStaticMap = parser.apiCatalogSync.getDelta(123).getStrictURLToMethods(); + + assertEquals(1,urlTemplateMap.size()); + assertEquals(0, urlStaticMap.size()); + } + + @Test + public void test20percentCondition() { + testInitializer(); + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + String url = "api/"; + List responseParams = new ArrayList<>(); + List urls = new ArrayList<>(); + for (int i=0; i< 50; i++) { + urls.add(url + "a"+i); + } + for (String c: urls) { + HttpResponseParams resp = createSampleParams("user1", c); + responseParams.add(resp); + } + + parser.syncFunction(responseParams.subList(0,23), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + parser.syncFunction(responseParams.subList(23,28), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + parser.syncFunction(responseParams.subList(28,33), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + + + Map urlTemplateMap = parser.apiCatalogSync.getDelta(123).getTemplateURLToMethods(); + Map urlStaticMap = parser.apiCatalogSync.getDelta(123).getStrictURLToMethods(); + + assertEquals(1,urlTemplateMap.keySet().size()); + assertEquals(0, urlStaticMap.keySet().size()); + + boolean merged = true; + for (SingleTypeInfo singleTypeInfo: SingleTypeInfoDao.instance.findAll(new BasicDBObject())) { + if (!singleTypeInfo.getUrl().equals("api/STRING")) { + merged = false; + break; + } + } + assertTrue(merged); + } + + + public static String createSimpleRequestPayload(String k) { + BasicDBObject ret = new BasicDBObject(); + + ret.append("id", 1).append("startDate", "some string"); + ret.append(k, "avneesh"); + ret.append("name", "ronaldo"); + ret.append("name1", "ronaldo"); + ret.append("name2", "ronaldo"); + ret.append("name3", "ronaldo"); + ret.append("name4", "ronaldo"); + ret.append("name5", "ronaldo"); + ret.append("name6", "ronaldo"); + ret.append("name7", "ronaldo"); + ret.append("name8", "ronaldo"); + ret.append("name9", "ronaldo"); + ret.append("name10", "ronaldo"); + + return ret.toJson(); + } + + public static String createDifferentResponsePayload(String k, int start) { + BasicDBObject ret = new BasicDBObject(); + + while (start < 10) { + ret.append(k+"_"+start, "Avneesh"); + start += 1; + } + + return ret.toJson(); + } + + public static HttpResponseParams createDifferentHttpResponseParams(int start, String url) { + HttpRequestParams httpRequestParams = new HttpRequestParams( + "GET", url, "", new HashMap<>(), createDifferentResponsePayload("req",start), 123 + ); + + return new HttpResponseParams( + "", 200, "", new HashMap<>(), createDifferentResponsePayload("resp", start), httpRequestParams, + 0,"1000000",false, HttpResponseParams.Source.MIRRORING,"", "" + ); + } + + + public static String createSimpleResponsePayload(String k) { + BasicDBObject ret = new BasicDBObject(); + + ret.append("a1", 1).append("b1", new BasicDBObject().append("a2", "some string").append("b2", "some number")); + ret.append(k, "ankita"); + ret.append("name", "ronaldo"); + ret.append("name1", "ronaldo"); + ret.append("name2", "ronaldo"); + ret.append("name3", "ronaldo"); + ret.append("name4", "ronaldo"); + ret.append("name5", "ronaldo"); + ret.append("name6", "ronaldo"); + ret.append("name7", "ronaldo"); + ret.append("name8", "ronaldo"); + ret.append("name9", "ronaldo"); + ret.append("name10", "ronaldo"); + + return ret.toJson(); + } + + public static HttpResponseParams createSampleParams(String userId, String url) { + HttpResponseParams ret = new HttpResponseParams(); + ret.type = "HTTP/1.1"; + ret.statusCode = 200; + ret.status = "OK"; + ret.headers = new HashMap<>(); + ret.headers.put("Access-Token", createList(" eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6ImFua3VzaEBnbWFpbC5jb20iLCJpYXQiOjE2MzQ3NTc0NzIsImV4cCI6MTYzNDc1ODM3Mn0.s_UihSzOlEY9spECqSS9SaTPxygn26YodRkYZKHmNwVnVkeppT5mQYKlwiUnjpKYIHxi2a82I50c0FbJKnTTk1Z5aYcT3t8GXUar_DaaEiv3eZZcZiqeOQSnPkP_c4nhC7Fqaq4g03p4Uj_7W5qvUAkTjHOCViwE933rmfX7tZA27o-9-b_ZKXYsTLfk-FjBV7f3piHmRo88j0WpkvuQc8LwcsoUq6yPclVuDsz9YHkvM1B33_QGdZ7nGz47M33tyLXqZyeF4qsnewkOOU6vCiDnM_eqbJghbZLSqP3Ut3lfA54BlAZ-HB5gLv-2HR0m_R2thDGXXE_G_onS-ZDB6A")); + ret.headers.put("Content-Type", createList(" application/json;charset=utf-8")); + ret.headers.put("Content-Length", createList(" 762")); + ret.headers.put("Server", createList(" Jetty(9.4.42.v20210604)")); + + ret.setPayload(createSimpleResponsePayload(url)); + ret.requestParams = new HttpRequestParams(); + + ret.requestParams.method = "POST"; + ret.requestParams.url = url; + ret.requestParams.type = "HTTP/1.1"; + Map> headers = new HashMap<>(); + List accessTokenHeaders = new ArrayList<>(); + accessTokenHeaders.add(userId); + headers.put("access-token", accessTokenHeaders); + headers.put("Host", createList("3.7.253.154:8080")); + headers.put("Connection", createList("keep-alive")); + headers.put("Content-Length", createList("61")); + headers.put("Access-Control-Allow-Origin", createList("*")); + headers.put("Accept", createList("application/json, text/plain, */*")); + headers.put("DNT", createList("1")); + headers.put("account", createList("222222")); + headers.put("User-Agent", createList("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36")); + headers.put("Content-Type", createList("application/json")); + headers.put("Origin", createList("http://3.7.253.154:8080")); + headers.put("Referer", createList("http://3.7.253.154:8080/dashboard/boards/1624886875")); + headers.put("Accept-Encoding", createList("gzip, deflate")); + headers.put("Accept-Language", createList("en-US,en;q=0.9,mr;q=0.8")); + headers.put("Cookie", createList("JSESSIONID=node01e7k0f9f2mkm0kyan971kl7bk7.node0; refreshToken=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjM0NzU3NDcwLCJleHAiOjE2MzQ4NDM4NzB9.MHoQpVFiYPgJrY-c4XrOlhWM20Qh1IOEKdiSne92k9p1YmUekBG7_z9osa9yYpO9Tsa1CeMs39ZDiS853boNJPAo6BcSswx6ReYHOmp3-qdu5dvqWjjQb0m-NNGGtPikvNi_d3MFmTQ0vKzu1n3WTmB_Iv-SPtmN22-Rees-VSnit6CQKvm_7kVQt-oU76LfIZ_KesfMm_vRHsFrHfKdw1zVT4XCSlPE0hJhbQNkzkwI-6zByYzG_5MnX5cyvUTIGgZ3-_VGxYRt8zPXFfAqgM1F3L4LDZSTLOu0I9gVElRP-JnSQRvYpsU0eVwP3cgS6UxxaSS_2zZU3Z_TPh8Qfg")); + + ret.requestParams.setHeaders(headers); + ret.requestParams.setPayload(createSimpleRequestPayload(url)); + ret.requestParams.setApiCollectionId(123); + ret.setOrig(ret.toString()); + return ret; + } + + @Test + public void testAllPaths() { + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + String url = "link/"; + + List responseParams = new ArrayList<>(); + + HttpResponseParams resp = TestDump2.createSampleParams("user1", url + 1); + ArrayList newHeader = new ArrayList<>(); + newHeader.add("hnew"); + resp.getHeaders().put("new header", newHeader); + responseParams.add(resp); + resp.setSource(HttpResponseParams.Source.HAR); + HttpCallParser parser = new HttpCallParser("access-token", 10, 40, 10, true); + + /* tryMergingWithKnownStrictURLs - put in delta-static */ + parser.syncFunction(responseParams, false, true); + assertTrue(parser.getSyncCount() == 0); + + /* processKnownStaticURLs */ + parser.syncFunction(responseParams, false, true); + + /* tryMergingWithKnownStrictURLs - merge with delta-static */ + responseParams.add(TestDump2.createSampleParams("user" + 2, url + 2)); + responseParams.add(TestDump2.createSampleParams("user" + 3, url + 3)); + + /* tryMergingWithKnownStrictURLs - merge with delta-template */ + responseParams.add(TestDump2.createSampleParams("user"+4, url+4)); + parser.syncFunction(responseParams, false, true); + assertTrue(parser.getSyncCount() == 0); + + /* tryMergingWithKnownTemplates */ + parser.syncFunction(responseParams, false, true); + assertTrue(parser.getSyncCount() == 0); + + /* tryMergingWithKnownStrictURLs - merge with Db url */ + url = "payment/"; + responseParams = new ArrayList<>(); + responseParams.add(TestDump2.createSampleParams("user" + 2, url + 2)); + responseParams.get(0).setSource(HttpResponseParams.Source.HAR); + parser.syncFunction(responseParams, false, true); + responseParams = new ArrayList<>(); + responseParams.add(TestDump2.createSampleParams("user" + 3, url + 3)); + + /* tryMergingWithKnownStrictURLs - merge with Db url - template already exists in delta */ + responseParams.add(TestDump2.createSampleParams("user" + 4, url + 4)); + responseParams.get(0).setSource(HttpResponseParams.Source.HAR); + parser.syncFunction(responseParams, false, true); + } + + @Test + public void testUrlParamSingleTypeInfoAndValues() { + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + String url = "api/"; + List responseParams = new ArrayList<>(); + List urls = new ArrayList<>(); + for (int i=0; i< 100; i++) { + urls.add(url + i + "/books/" + (i+1) + "/cars/" + (i+3)); + } + for (String c: urls) { + BasicDBObject ret = new BasicDBObject(); + ret.put("name", c); + HttpRequestParams httpRequestParams = new HttpRequestParams("GET", c, "", new HashMap<>(), ret.toJson(), 123); + HttpResponseParams resp = new HttpResponseParams("", 200,"", new HashMap<>(), ret.toJson(),httpRequestParams, 0,"0",false, HttpResponseParams.Source.MIRRORING,"", ""); + responseParams.add(resp); + } + + parser.syncFunction(responseParams.subList(0,10), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + + // dbState doesn't have any template URLs initially so no urlParams are considered + testSampleSizeAndDomainOfSti(parser,0, 10, SingleTypeInfo.Domain.ENUM, SingleTypeInfo.Domain.ENUM); + + parser.syncFunction(responseParams.subList(10,55), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + + // Now dbState has template URLs so urlParam values are now stored + assertEquals(0,getStaticURLsSize(parser)); + testSampleSizeAndDomainOfSti(parser, 45, 55, SingleTypeInfo.Domain.ENUM, SingleTypeInfo.Domain.ENUM); + + parser.apiCatalogSync.syncWithDB(false, true); + parser.apiCatalogSync.syncWithDB(false, true); + + testSampleSizeAndDomainOfSti(parser, 45, 0, SingleTypeInfo.Domain.ENUM, SingleTypeInfo.Domain.ANY); + + // changing the parser symbolizes instance restart + // using the new or old parser shouldn't change the result + HttpCallParser parserNew = new HttpCallParser("userIdentifier", 1, 1, 1, true); + parserNew.syncFunction(responseParams.subList(55,70), false, true); + parserNew.apiCatalogSync.syncWithDB(false, true); + parserNew.syncFunction(responseParams.subList(70,100), false, true); + parserNew.apiCatalogSync.syncWithDB(false, true); + parserNew.apiCatalogSync.syncWithDB(false, true); + + // both now range + testSampleSizeAndDomainOfSti(parserNew, 0, 0, SingleTypeInfo.Domain.RANGE, SingleTypeInfo.Domain.ANY); + + + } + + private void testSampleSizeAndDomainOfSti(HttpCallParser parser, int urlParamValuesSize, int nonUrlParamValuesSize, + SingleTypeInfo.Domain urlParamDomain, SingleTypeInfo.Domain nonUrlParamDomain) { + Map templateURLToMethods = parser.apiCatalogSync.getDbState(123).getTemplateURLToMethods(); + for (RequestTemplate requestTemplate: templateURLToMethods.values()) { + for (SingleTypeInfo singleTypeInfo: requestTemplate.getAllTypeInfo()) { + if (singleTypeInfo.getIsUrlParam()) { + assertEquals(urlParamValuesSize, singleTypeInfo.getValues().getElements().size()); + assertEquals(urlParamDomain,singleTypeInfo.getDomain()); + } else { + assertEquals(nonUrlParamValuesSize, singleTypeInfo.getValues().getElements().size()); + assertEquals(nonUrlParamDomain, singleTypeInfo.getDomain()); + } + } + } + } + + @Test + public void testMinMaxAndLastSeenNew() { + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + String url = "api/"; + + // test for 1 url + HttpResponseParams httpResponseParams1 = createHttpResponseForMinMax(url+"books1", 23.4F,-98F ); + HttpResponseParams httpResponseParams2 = createHttpResponseForMinMax(url+"books1", 2.3F,-200.5F ); + HttpResponseParams httpResponseParams3 = createHttpResponseForMinMax(url+"books1", 2500.9F,-200F ); + parser.syncFunction(Arrays.asList(httpResponseParams1, httpResponseParams2, httpResponseParams3), false, true); + + parser.apiCatalogSync.syncWithDB(false, true); + Collection requestTemplates = parser.apiCatalogSync.getDbState(123).getStrictURLToMethods().values(); + validateMinMax(requestTemplates, 2500, 2, -98, -200); + + // merge the urls + float reqMax = 2500.9f; + float reqMin = 2.3f; + float respMax = -98f; + float respMin = -200.5f; + for (int i=0; i< APICatalogSync.STRING_MERGING_THRESHOLD; i++) { + reqMax += 1; + reqMin -= 1; + respMax += 1; + respMin -= 1; + HttpResponseParams httpResponseParams = createHttpResponseForMinMax(url+"books"+i, reqMax, respMax); + parser.syncFunction(Collections.singletonList(httpResponseParams), false, true); + + httpResponseParams = createHttpResponseForMinMax(url+"books"+i, reqMin, respMin); + parser.syncFunction(Collections.singletonList(httpResponseParams), false, true); + } + parser.apiCatalogSync.syncWithDB(false, true); + + HttpResponseParams httpResponseParams = createHttpResponseForMinMax(url+"books99", 190f, -190f); + parser.syncFunction(Collections.singletonList(httpResponseParams), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + + httpResponseParams = createHttpResponseForMinMax(url+"books100", 190f, -190f); + parser.syncFunction(Collections.singletonList(httpResponseParams), false, true); + parser.apiCatalogSync.syncWithDB(false, true); + + // changing the parser symbolizes instance restart + // using the new or old parser shouldn't change the result + HttpCallParser parserNew = new HttpCallParser("userIdentifier", 1, 1, 1, true); + + assertEquals(0,parserNew.apiCatalogSync.getDbState(123).getStrictURLToMethods().size()); + assertEquals(1,parserNew.apiCatalogSync.getDbState(123).getTemplateURLToMethods().size()); + + requestTemplates = parserNew.apiCatalogSync.getDbState(123).getTemplateURLToMethods().values(); + validateMinMax(requestTemplates, Double.valueOf(reqMax+"").longValue(), Double.valueOf(reqMin+"").longValue(), Double.valueOf(respMax+"").longValue(), Double.valueOf(respMin+"").longValue()); + + httpResponseParams = createHttpResponseForMinMax(url+"books10", 19000f, -190f); + parserNew.syncFunction(Collections.singletonList(httpResponseParams), false, true); + httpResponseParams = createHttpResponseForMinMax(url+"books15", 19f, -19000f); + parserNew.syncFunction(Collections.singletonList(httpResponseParams), false, true); + parserNew.apiCatalogSync.syncWithDB(false, true); + requestTemplates = parserNew.apiCatalogSync.getDbState(123).getTemplateURLToMethods().values(); + validateMinMax(requestTemplates, 19000, Double.valueOf(reqMin+"").longValue(), Double.valueOf(respMax+"").longValue(), -19000); + + } + + private HttpResponseParams createHttpResponseForMinMax(String url, float reqPayload, float respPayload) { + BasicDBObject reqRet = new BasicDBObject(); + reqRet.put("value", reqPayload); + BasicDBObject respRet = new BasicDBObject(); + respRet.put("value", respPayload); + + HttpRequestParams httpRequestParams = new HttpRequestParams("GET", url, "", new HashMap<>(), reqRet.toJson(), 123); + return new HttpResponseParams("", 200,"", new HashMap<>(), respRet.toJson(),httpRequestParams, 0,"0",false, HttpResponseParams.Source.MIRRORING,"", ""); + } + + private void validateMinMax(Collection requestTemplateCollections, long reqMaxValue, long reqMinValue, + long respMaxValue, long respMinValue) { + for (RequestTemplate requestTemplate: requestTemplateCollections) { + for (SingleTypeInfo singleTypeInfo: requestTemplate.getAllTypeInfo()) { + if (singleTypeInfo.isIsHeader() || singleTypeInfo.getIsUrlParam()) continue; + if (singleTypeInfo.getResponseCode() == -1) { + assertEquals(reqMaxValue, singleTypeInfo.getMaxValue()); + assertEquals(reqMinValue, singleTypeInfo.getMinValue()); + } else { + assertEquals(respMaxValue, singleTypeInfo.getMaxValue()); + assertEquals(respMinValue, singleTypeInfo.getMinValue()); + } + } + } + } + + // Test to check if param dbUpdates are made only when certain conditions are true + // Case when update shouldn't be made: + // delta doesn't have the strict url but db has it and no change in min, max and last seen not older than 30 mins + @Test + public void testDbUpdateParams() { + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1, 1, true); + int collectionId = 123; + String url = "api/"; + + HttpResponseParams httpResponseParams1 = createHttpResponseForMinMax(url+"books1", 23.4F,-98F ); + parser.syncFunction(Collections.singletonList(httpResponseParams1),true, true); + assertEquals(1, parser.apiCatalogSync.getDbState(collectionId).getStrictURLToMethods().size()); + + APICatalogSync.DbUpdateReturn dbUpdateReturn1 = cleanSync(httpResponseParams1, collectionId); + assertEquals(0, dbUpdateReturn1.bulkUpdatesForSingleTypeInfo.size()); // because no change in minMax + + HttpResponseParams httpResponseParams2 = createHttpResponseForMinMax(url+"books1", 230.4F,-98F ); + APICatalogSync.DbUpdateReturn dbUpdateReturn2 = cleanSync(httpResponseParams2, collectionId); + assertEquals(1, dbUpdateReturn2.bulkUpdatesForSingleTypeInfo.size()); // because reqPayload Max changed + + HttpResponseParams httpResponseParams3 = createHttpResponseForMinMax(url+"books1", 100,-98F ); + APICatalogSync.DbUpdateReturn dbUpdateReturn3 = cleanSync(httpResponseParams3, collectionId); + assertEquals(1, dbUpdateReturn3.bulkUpdatesForSingleTypeInfo.size()); // even though minMax didn't change new values were added + } + + // this function takes httpResponseParam and does runtime thingy in a clean environment (equivalent to server restart) + private APICatalogSync.DbUpdateReturn cleanSync(HttpResponseParams httpResponseParams, int collectionId) { + // new httpCallParser to make sure delta is clean + HttpCallParser parser = new HttpCallParser("userIdentifier", 1, 1000, Context.now() + 1000, true); + parser.numberOfSyncs = 1000; // to make sure it doesn't sync before + + parser.syncFunction(Collections.singletonList(httpResponseParams),false, true); + APICatalogSync apiCatalogSync = parser.apiCatalogSync; + return apiCatalogSync.getDBUpdatesForParams( + apiCatalogSync.getDelta(collectionId), apiCatalogSync.getDbState(collectionId), false + ); + + } + + // testing if new endpoints are getting their sample data synced immediately or not + @Test + public void testSampleDataUpdate() throws Exception { + testInitializer(); + SingleTypeInfoDao.instance.getMCollection().drop(); + SampleDataDao.instance.getMCollection().drop(); + + HttpCallParser httpCallParser = new HttpCallParser("", 100000, 10000, 10000, true); + + // url = https://petstore.swagger.io/v2/pet; method = put + String payload1 = "{\"method\":\"PUT\",\"requestPayload\":\"{\\\"photoUrls\\\":[\\\"string\\\"],\\\"name\\\":\\\"doggie\\\",\\\"id\\\":0,\\\"category\\\":{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"},\\\"tags\\\":[{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"}],\\\"status\\\":\\\"available\\\"}\",\"responsePayload\":\"{\\\"id\\\":9223372036854775807,\\\"category\\\":{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"},\\\"name\\\":\\\"doggie\\\",\\\"photoUrls\\\":[\\\"string\\\"],\\\"tags\\\":[{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"}],\\\"status\\\":\\\"available\\\"}\",\"ip\":\"null\",\"source\":\"MIRRORING\",\"type\":\"HTTP/2\",\"akto_vxlan_id\":\"1661807253\",\"path\":\"https://petstore.swagger.io/v2/pet\",\"requestHeaders\":\"{\\\"Origin\\\":\\\"https://petstore.swagger.io\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"TE\\\":\\\"trailers\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Content-Length\\\":\\\"215\\\",\\\"Content-Type\\\":\\\"application/json\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:11:58 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\",\"time\":\"1641327118\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + HttpResponseParams httpResponseParams1 = HttpCallParser.parseKafkaMessage(payload1); + Bson filter1 = Filters.and( + Filters.eq("_id.url", "https://petstore.swagger.io/v2/pet"), + Filters.eq("_id.method", "PUT") + ); + + httpCallParser.syncFunction(Collections.singletonList(httpResponseParams1),false, true); + httpCallParser.apiCatalogSync.syncWithDB(false, true); + + SampleData sampleData1 = SampleDataDao.instance.findOne(filter1); + assertEquals(1, sampleData1.getSamples().size()); + + // payload1 but with different request body. This won't get updated because not new URL + String payload1UpdatedRequestPayload = "{\"method\":\"PUT\",\"requestPayload\":\"{\\\"photoUrls\\\":[\\\"string\\\"],\\\"name\\\":\\\"tommie\\\",\\\"id\\\":0,\\\"category\\\":{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"},\\\"tags\\\":[{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"}],\\\"status\\\":\\\"available\\\"}\",\"responsePayload\":\"{\\\"id\\\":9223372036854775807,\\\"category\\\":{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"},\\\"name\\\":\\\"doggie\\\",\\\"photoUrls\\\":[\\\"string\\\"],\\\"tags\\\":[{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"}],\\\"status\\\":\\\"available\\\"}\",\"ip\":\"null\",\"source\":\"MIRRORING\",\"type\":\"HTTP/2\",\"akto_vxlan_id\":\"1661807253\",\"path\":\"https://petstore.swagger.io/v2/pet\",\"requestHeaders\":\"{\\\"Origin\\\":\\\"https://petstore.swagger.io\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"TE\\\":\\\"trailers\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Content-Length\\\":\\\"215\\\",\\\"Content-Type\\\":\\\"application/json\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:11:58 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\",\"time\":\"1641327118\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + HttpResponseParams httpResponseParams2 = HttpCallParser.parseKafkaMessage(payload1UpdatedRequestPayload); + httpCallParser.syncFunction(Collections.singletonList(httpResponseParams2), false, true); + httpCallParser.apiCatalogSync.syncWithDB(false, true); + + sampleData1 = SampleDataDao.instance.findOne(filter1); + assertEquals(1, sampleData1.getSamples().size()); + + + // url = https://petstore.swagger.io/v2/books/1 ; method = post + String payload2 = "{\"method\":\"POST\",\"requestPayload\":\"{\\\"photoUrls\\\":[\\\"string\\\"],\\\"name\\\":\\\"tommie\\\",\\\"id\\\":0,\\\"category\\\":{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"},\\\"tags\\\":[{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"}],\\\"status\\\":\\\"available\\\"}\",\"responsePayload\":\"{\\\"id\\\":9223372036854775807,\\\"category\\\":{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"},\\\"name\\\":\\\"doggie\\\",\\\"photoUrls\\\":[\\\"string\\\"],\\\"tags\\\":[{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"}],\\\"status\\\":\\\"available\\\"}\",\"ip\":\"null\",\"source\":\"MIRRORING\",\"type\":\"HTTP/2\",\"akto_vxlan_id\":\"1661807253\",\"path\":\"https://petstore.swagger.io/v2/books/1\",\"requestHeaders\":\"{\\\"Origin\\\":\\\"https://petstore.swagger.io\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"TE\\\":\\\"trailers\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Content-Length\\\":\\\"215\\\",\\\"Content-Type\\\":\\\"application/json\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:11:58 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\",\"time\":\"1641327118\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + HttpResponseParams httpResponseParams3 = HttpCallParser.parseKafkaMessage(payload2); + httpCallParser.syncFunction(Collections.singletonList(httpResponseParams3), false, true); + httpCallParser.apiCatalogSync.syncWithDB(false, true); + + Bson filter2 = Filters.and( + Filters.eq("_id.url", "https://petstore.swagger.io/v2/books/1"), + Filters.eq("_id.method", "POST") + ); + + SampleData sampleData2 = SampleDataDao.instance.findOne(filter2); + assertEquals(1, sampleData2.getSamples().size()); + + // url = https://petstore.swagger.io/v2/books/2 ; method = post + String payload3 = "{\"method\":\"POST\",\"requestPayload\":\"{\\\"photoUrls\\\":[\\\"string\\\"],\\\"name\\\":\\\"charlie\\\",\\\"id\\\":0,\\\"category\\\":{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"},\\\"tags\\\":[{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"}],\\\"status\\\":\\\"available\\\"}\",\"responsePayload\":\"{\\\"id\\\":9223372036854775807,\\\"category\\\":{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"},\\\"name\\\":\\\"doggie\\\",\\\"photoUrls\\\":[\\\"string\\\"],\\\"tags\\\":[{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"}],\\\"status\\\":\\\"available\\\"}\",\"ip\":\"null\",\"source\":\"MIRRORING\",\"type\":\"HTTP/2\",\"akto_vxlan_id\":\"1661807253\",\"path\":\"https://petstore.swagger.io/v2/books/2\",\"requestHeaders\":\"{\\\"Origin\\\":\\\"https://petstore.swagger.io\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"TE\\\":\\\"trailers\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Content-Length\\\":\\\"215\\\",\\\"Content-Type\\\":\\\"application/json\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:11:58 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\",\"time\":\"1641327118\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + HttpResponseParams httpResponseParams4 = HttpCallParser.parseKafkaMessage(payload3); + httpCallParser.syncFunction(Collections.singletonList(httpResponseParams4), false, true); + httpCallParser.apiCatalogSync.syncWithDB(false, true); + + Bson filter3 = Filters.and( + Filters.eq("_id.url", "https://petstore.swagger.io/v2/books/INTEGER"), + Filters.eq("_id.method", "POST") + ); + + SampleData sampleData3 = SampleDataDao.instance.findOne(filter3); + assertEquals(1, sampleData3.getSamples().size()); + + // url = https://petstore.swagger.io/v2/books/3 ; method = post + // sample data count won't increase because not new url + String payload4 = "{\"method\":\"POST\",\"requestPayload\":\"{\\\"photoUrls\\\":[\\\"string\\\"],\\\"name\\\":\\\"brandon\\\",\\\"id\\\":0,\\\"category\\\":{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"},\\\"tags\\\":[{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"}],\\\"status\\\":\\\"available\\\"}\",\"responsePayload\":\"{\\\"id\\\":9223372036854775807,\\\"category\\\":{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"},\\\"name\\\":\\\"doggie\\\",\\\"photoUrls\\\":[\\\"string\\\"],\\\"tags\\\":[{\\\"id\\\":0,\\\"name\\\":\\\"string\\\"}],\\\"status\\\":\\\"available\\\"}\",\"ip\":\"null\",\"source\":\"MIRRORING\",\"type\":\"HTTP/2\",\"akto_vxlan_id\":\"1661807253\",\"path\":\"https://petstore.swagger.io/v2/books/3\",\"requestHeaders\":\"{\\\"Origin\\\":\\\"https://petstore.swagger.io\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"TE\\\":\\\"trailers\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Content-Length\\\":\\\"215\\\",\\\"Content-Type\\\":\\\"application/json\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:11:58 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\",\"time\":\"1641327118\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + HttpResponseParams httpResponseParams5 = HttpCallParser.parseKafkaMessage(payload4); + httpCallParser.syncFunction(Collections.singletonList(httpResponseParams5), false, true); + httpCallParser.apiCatalogSync.syncWithDB(false, true); + + SampleData sampleData4 = SampleDataDao.instance.findOne(filter3); + assertEquals(1, sampleData4.getSamples().size()); + + } + +} diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/FlowTest.java b/apps/api-runtime/src/test/java/com/akto/runtime/FlowTest.java new file mode 100644 index 0000000000..21326958f5 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/FlowTest.java @@ -0,0 +1,66 @@ +package com.akto.runtime; + +import com.akto.dto.HttpRequestParams; +import org.junit.Test; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class FlowTest { + + @Test + public void testGetUserIdentifier() { + Map> headers = new HashMap<>(); + String name = "Access-Token"; + String value = "fwefwieofjweofiew"; + headers.put(name, Arrays.asList(value, "wefiowjefew")); + HttpRequestParams requestParams = new HttpRequestParams( + "get", "/api/some", "Http",headers ,"", 0 + ); + String u = null; + try { + u = Flow.getUserIdentifier(name, requestParams); + } catch (Exception e) { + ; + } + + assertEquals(value, u); + } + + @Test + public void testGetUserIdentifierWithoutToken() { + Map> headers = new HashMap<>(); + String name = "Access-Token"; + String value = "fwefwieofjweofiew"; + headers.put(name, Arrays.asList(value)); + HttpRequestParams requestParams = new HttpRequestParams( + "get", "/api/some", "Http",headers ,"", 0 + ); + String u = null; + try { + u = Flow.getUserIdentifier("wefwe", requestParams); + } catch (Exception e) { + } + + assertNull(u); + } + + @Test + public void testGetUserIdentifierEmptyList() { + Map> headers = new HashMap<>(); + String name = "Access-Token"; + headers.put(name, new ArrayList<>()); + HttpRequestParams requestParams = new HttpRequestParams( + "get", "/api/some", "Http",headers ,"",0 + ); + String u = null; + try { + u = Flow.getUserIdentifier("wefwe", requestParams); + } catch (Exception e) { + } + + assertNull(u); + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/MarkovTest.java b/apps/api-runtime/src/test/java/com/akto/runtime/MarkovTest.java new file mode 100644 index 0000000000..39951b53ce --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/MarkovTest.java @@ -0,0 +1,111 @@ +package com.akto.runtime; + +import com.akto.dao.context.Context; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.Markov; +import com.akto.dto.HttpResponseParams.Source; +import com.akto.dto.HttpRequestParams; + + +import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.*; + +public class MarkovTest { + + @Test + public void testBuildMarkovFromDb() { + List markovList = new ArrayList<>(); + Markov.State state1 = new Markov.State("api/a", "get"); + Markov.State state2 = new Markov.State("api/b", "post"); + markovList.add(new Markov(state1, state2, new HashMap<>(), new HashSet<>())); + MarkovSync markovSync = new MarkovSync(10,10,10); + markovSync.buildMarkovFromDb(markovList); + Map> markovFromDb = markovSync.markovFromDb; + assertEquals(1,markovFromDb.keySet().size()); + assertEquals(1,markovFromDb.get(state1.hashCode()+"").keySet().size()); + assertNotNull(markovFromDb.get(state1.hashCode()+"").get(state2.hashCode()+"")); + } + public Map> generateHeaders(String userId) { + Map> headers = new HashMap<>(); + String name = "Access-Token"; + headers.put(name, Collections.singletonList(userId)); + return headers; + } + + public HttpResponseParams generateHttpResponseParamsForMarkov(String url, String method, String userId) { + + HttpRequestParams httpRequestParams = new HttpRequestParams( + method, url, "",generateHeaders(userId),"", 0 + ); + return new HttpResponseParams( + "",200,"ok", generateHeaders(userId), "", httpRequestParams, Context.now(), "1111", false, Source.OTHER, "", "" + ); + } + + @Test + public void testNullResponses() { + MarkovSync markovSync = new MarkovSync(2,100,100000000); + markovSync.buildMarkov(null,"Access-Token"); + assertEquals(0,markovSync.markovMap.keySet().size()); + } + + @Test + public void safd() { + List resp = new ArrayList<>(); + resp.add(generateHttpResponseParamsForMarkov("api/a", "get", "1")); + resp.add(generateHttpResponseParamsForMarkov("api/b", "post", "1")); + resp.add(generateHttpResponseParamsForMarkov("api/c", "get", "1")); + resp.add(generateHttpResponseParamsForMarkov("api/d", "get", "1")); + resp.add(generateHttpResponseParamsForMarkov("api/b", "post", "1")); + resp.add(generateHttpResponseParamsForMarkov("api/c", "get", "1")); + resp.add(generateHttpResponseParamsForMarkov("api/d", "get", "2")); + resp.add(generateHttpResponseParamsForMarkov("api/a", "get", "3")); + resp.add(generateHttpResponseParamsForMarkov("api/b", "post", "3")); + + MarkovSync markovSync = new MarkovSync(2,100,100000000); + markovSync.buildMarkov(resp,"Access-Token"); + Map markov = markovSync.markovMap; + + assertEquals(4,markov.keySet().size()); + + String todayKey = Flow.calculateTodayKey(); + Markov.State state1 = new Markov.State("api/a", "get"); + Markov.State state2 = new Markov.State("api/b", "post"); + String key = new Markov(state1, state2, new HashMap<>(), new HashSet<>()).hashCode() + ""; + assertEquals(2,markov.get(key).getCountMap().get(todayKey)); + assertEquals(2,markov.get(key).getUserIds().size()); + + state1 = new Markov.State("api/b", "post"); + state2 = new Markov.State("api/c", "get"); + key = new Markov(state1, state2, new HashMap<>(), new HashSet<>()).hashCode() + ""; + assertEquals(2,markov.get(key).getCountMap().get(todayKey)); + assertEquals(1,markov.get(key).getUserIds().size()); + + state1 = new Markov.State("api/c", "get"); + state2 = new Markov.State("api/d", "get"); + key = new Markov(state1, state2, new HashMap<>(), new HashSet<>()).hashCode() + ""; + assertEquals(1,markov.get(key).getCountMap().get(todayKey)); + assertEquals(1,markov.get(key).getUserIds().size()); + + markovSync.getBulkUpdates(); + + state1 = new Markov.State("api/a", "get"); + state2 = new Markov.State("api/b", "post"); + key = new Markov(state1, state2, new HashMap<>(), new HashSet<>()).hashCode() + ""; + Markov markov1 = markov.get(key); + assertEquals(0,markov1.getCountMap().keySet().size()); + assertEquals(0,markov1.getUserIds().size()); + + state1 = new Markov.State("api/b", "post"); + state2 = new Markov.State("api/c", "get"); + key = new Markov(state1, state2, new HashMap<>(), new HashSet<>()).hashCode() + ""; + assertEquals(2,markov.get(key).getCountMap().get(todayKey)); + assertEquals(1,markov.get(key).getUserIds().size()); + + } + +} + diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/RelationshipTest.java b/apps/api-runtime/src/test/java/com/akto/runtime/RelationshipTest.java new file mode 100644 index 0000000000..45a3eeaa44 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/RelationshipTest.java @@ -0,0 +1,168 @@ +package com.akto.runtime; + + +import com.akto.DaoInit; +import com.akto.dao.context.Context; +import com.akto.dto.Relationship; +import com.akto.parsers.HttpCallParser; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.HttpResponseParams.Source; +import com.akto.dto.HttpRequestParams; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.ConnectionString; +import org.junit.Test; + +import java.io.IOException; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class RelationshipTest { + ObjectMapper mapper = new ObjectMapper(); + JsonFactory factory = mapper.getFactory(); + @Test + public void testExtractAllValuesFromPayloadNullCase() { + Map> values= new HashMap<>(); + RelationshipSync.extractAllValuesFromPayload(null, new ArrayList<>(), values); + assertEquals(0,values.keySet().size()); + } + + @Test + public void testExtractAllValuesFromPayload1() throws IOException { + JsonParser jp = factory.createParser("{\"name\":\"avneesh\", \"age\":99, \"finalAge\": 99}"); + JsonNode node = mapper.readTree(jp); + Map> values= new HashMap<>(); + RelationshipSync.extractAllValuesFromPayload(node, new ArrayList<>(), values); + assertEquals(3,values.keySet().size()); + assertTrue(values.get("name").contains("avneesh")); + assertTrue(values.get("age").contains("99")); + assertTrue(values.get("finalAge").contains("99")); + } + + @Test + public void testExtractAllValuesFromPayload2() throws IOException { + JsonParser jp = factory.createParser("[{\"name\":\"avneesh\", \"age\":99, \"finalAge\": 99}]"); + JsonNode node = mapper.readTree(jp); + Map> values= new HashMap<>(); + RelationshipSync.extractAllValuesFromPayload(node, new ArrayList<>(), values); + assertEquals(3,values.keySet().size()); + assertTrue(values.get("$#name").contains("avneesh")); + assertTrue(values.get("$#age").contains("99")); + assertTrue(values.get("$#finalAge").contains("99")); + } + + @Test + public void testExtractAllValuesFromPayload3() throws IOException { + JsonParser jp = factory.createParser("{\"interests\": [{\"name\":\"football\"}]}"); + JsonNode node = mapper.readTree(jp); + Map> values= new HashMap<>(); + RelationshipSync.extractAllValuesFromPayload(node, new ArrayList<>(), values); + assertEquals(1,values.keySet().size()); + assertTrue(values.get("interests#$#name").contains("football")); + } + + @Test + public void testExtractAllValuesFromPayload4() throws IOException { + JsonParser jp = factory.createParser("{\"name\":\"null\", \"age\":99, \"finalAge\": 99, \"hobby\":\" \"}"); + JsonNode node = mapper.readTree(jp); + Map> values= new HashMap<>(); + RelationshipSync.extractAllValuesFromPayload(node, new ArrayList<>(), values); + assertEquals(4,values.keySet().size()); + assertTrue(values.get("age").contains("99")); + assertTrue(values.get("finalAge").contains("99")); + } + + public Map> generateHeaders(String userId) { + Map> headers = new HashMap<>(); + String name = "Access-Token"; + headers.put(name, Collections.singletonList(userId)); + return headers; + } + + public HttpResponseParams generateHttpResponseParamsForRelationship(String url, String method, + String userId, int statusCode, + String reqPayload, String respPayload) { + + HttpRequestParams httpRequestParams = new HttpRequestParams( + method, url, "",generateHeaders(userId),reqPayload, 0 + ); + return new HttpResponseParams( + "",statusCode,"ok", generateHeaders(userId), respPayload, httpRequestParams, Context.now(), "1111",false, Source.OTHER, "", "" + ); + } + + @Test + public void testBuildParameterMap() throws Exception { + List respList = new ArrayList<>(); + String req, resp; + + req = "{\"name\":\"avneesh\", \"age\":99}"; + resp = "{\"status\":\"done\", \"id\": 11111}"; + respList.add(generateHttpResponseParamsForRelationship("api/a", "post", "1",200,req,resp)); + + req = "{\"name\":\"ankush\", \"age\":93}"; + resp = "{\"status\":\"done\", \"id\": 22222}"; + respList.add(generateHttpResponseParamsForRelationship("api/a", "post", "2",200,req,resp)); + + req = "{\"user_id\":\"11111\"}"; + resp = "{\"familyName\":\"hota\"}"; + respList.add(generateHttpResponseParamsForRelationship("api/b", "get", "1",200,req,resp)); + + req = "{\"user_id\":\"22222\"}"; + resp = "{\"familyName\":\"jain\"}"; + respList.add(generateHttpResponseParamsForRelationship("api/b", "get", "2",200,req,resp)); + + req = "{\"user_id\":\"22222\"}"; + resp = "{\"familyName\":\"jain\"}"; + respList.add(generateHttpResponseParamsForRelationship("api/b", "get", "2",200,req,resp)); + + req = "{\"familyName\":\"jain\"}"; + resp = "{\"state\":\"Rajasthan\"}"; + respList.add(generateHttpResponseParamsForRelationship("api/c", "get", "2",200,req,resp)); + + RelationshipSync relationshipSync = new RelationshipSync(2,100,1000000); + for (HttpResponseParams responseParams: respList) { + relationshipSync.buildParameterMap(responseParams,"Access-Token"); + } + + + Map>>> userWiseParameterMap = relationshipSync.userWiseParameterMap; + + assertEquals(2, userWiseParameterMap.keySet().size()); + assertEquals(5, userWiseParameterMap.get("1").keySet().size()); + assertEquals(6, userWiseParameterMap.get("2").keySet().size()); + assertEquals(1, userWiseParameterMap.get("1").get("11111").get("request").size()); + assertEquals("api/b", userWiseParameterMap.get("1").get("11111").get("request").iterator().next().getUrl()); + assertEquals(1, userWiseParameterMap.get("1").get("11111").get("response").size()); + assertEquals("api/a", userWiseParameterMap.get("1").get("11111").get("response").iterator().next().getUrl()); + assertEquals(1, userWiseParameterMap.get("1").get("avneesh").get("request").size()); + assertEquals(0, userWiseParameterMap.get("1").get("avneesh").get("response").size()); + assertEquals(1, userWiseParameterMap.get("2").get("jain").get("request").size()); + assertEquals(1, userWiseParameterMap.get("2").get("jain").get("response").size()); + + relationshipSync.buildRelationships(); + + Map relations = relationshipSync.relations; + assertEquals(relations.size(), 2); + + Relationship.ApiRelationInfo parent = new Relationship.ApiRelationInfo("api/a","post", false,"id" ,200); + Relationship.ApiRelationInfo child = new Relationship.ApiRelationInfo("api/b","get", false, "user_id",200); + String key = parent.hashCode() + "." + child.hashCode(); + assertEquals(2,relations.get(key).getUserIds().size()); + assertEquals(2,relations.get(key).getCountMap().get(Flow.calculateTodayKey())); + + relationshipSync.getBulkUpdates(); + assertEquals(2, relations.size()); + + parent = new Relationship.ApiRelationInfo("api/b","get", false,"familyName" ,200); + child = new Relationship.ApiRelationInfo("api/c","get", false, "familyName",200); + key = parent.hashCode() + "." + child.hashCode(); + assertEquals(1,relations.get(key).getUserIds().size()); + assertEquals(1,relations.get(key).getCountMap().get(Flow.calculateTodayKey())); + + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/TestApiCatalogSync.java b/apps/api-runtime/src/test/java/com/akto/runtime/TestApiCatalogSync.java new file mode 100644 index 0000000000..b1339a8571 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/TestApiCatalogSync.java @@ -0,0 +1,199 @@ +package com.akto.runtime; + +import com.akto.MongoBasedTest; +import com.akto.dao.*; +import com.akto.dto.AktoDataType; +import com.akto.dto.ApiInfo; +import com.akto.dto.FilterSampleData; +import com.akto.dto.IgnoreData; +import com.akto.dto.SensitiveParamInfo; +import com.akto.dto.SensitiveSampleData; +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.traffic.TrafficInfo; +import com.akto.dto.type.*; +import com.akto.runtime.merge.MergeSimilarUrls; +import com.akto.types.CappedSet; +import com.mongodb.BasicDBObject; + +import org.junit.Ignore; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class TestApiCatalogSync extends MongoBasedTest { + public void testInitializer(){ + SingleTypeInfo.aktoDataTypeMap = new HashMap<>(); + SingleTypeInfo.aktoDataTypeMap.put("JWT", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("PHONE_NUMBER", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("CREDIT_CARD", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("IP_ADDRESS", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("EMAIL", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("SSN", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("UUID", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + } + + @Test + public void testFillUrlParams() { + testInitializer(); + RequestTemplate requestTemplate1 = new RequestTemplate(new HashMap<>(), new HashMap<>(), new HashMap<>(), new TrafficRecorder()); + validateSubTypeAndMinMax(requestTemplate1, "/api/books/378282246310005", "/api/books/STRING", + SingleTypeInfo.CREDIT_CARD, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE, 1,1); + validateSubTypeAndMinMax(requestTemplate1, "api/books/378282246310005", "/api/books/STRING", + SingleTypeInfo.CREDIT_CARD, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE, 2, 1); + validateSubTypeAndMinMax(requestTemplate1, "api/books/4111111111111111/", "/api/books/STRING", + SingleTypeInfo.CREDIT_CARD, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE, 3, 2); + + RequestTemplate requestTemplate2 = new RequestTemplate(new HashMap<>(), new HashMap<>(), new HashMap<>(), new TrafficRecorder()); + validateSubTypeAndMinMax(requestTemplate2, "/api/books/234", "/api/books/INTEGER", + SingleTypeInfo.INTEGER_32, 234, 234, 1, 1); + validateSubTypeAndMinMax(requestTemplate2, "api/books/999", "/api/books/INTEGER", + SingleTypeInfo.INTEGER_32, 234,999, 2, 2); + } + + private void validateSubTypeAndMinMax(RequestTemplate requestTemplate, String url, String templateUrl, + SingleTypeInfo.SubType subType, long minValue, long maxValue, int count, + int valuesCount) { + + String[] tokenizedUrl = APICatalogSync.tokenize(url); + URLTemplate urlTemplate = APICatalogSync.createUrlTemplate(templateUrl, URLMethods.Method.GET); + + requestTemplate.fillUrlParams(tokenizedUrl, urlTemplate, 0); + + assertEquals(1, requestTemplate.getUrlParams().size()); + KeyTypes keyTypes = requestTemplate.getUrlParams().get(2); + SingleTypeInfo singleTypeInfo= keyTypes.getOccurrences().get(subType); + assertEquals(subType, singleTypeInfo.getSubType()); + assertEquals(maxValue, singleTypeInfo.getMaxValue()); + assertEquals(minValue, singleTypeInfo.getMinValue()); + assertEquals(count, singleTypeInfo.getCount()); + assertEquals(valuesCount, singleTypeInfo.getValues().getElements().size()); + } + + + @Test + public void testMergeAndUpdateDb() { + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiInfoDao.instance.getMCollection().drop(); + SampleDataDao.instance.getMCollection().drop(); + TrafficInfoDao.instance.getMCollection().drop(); + SensitiveSampleDataDao.instance.getMCollection().drop(); + SensitiveParamInfoDao.instance.getMCollection().drop(); + FilterSampleDataDao.instance.getMCollection().drop(); + + Set toMergeUrls = new HashSet<>(); + toMergeUrls.add("/api/books/1"); + toMergeUrls.add("/api/books/2"); + toMergeUrls.add("/api/books/3"); + String mergedUrl = "/api/books/INTEGER"; + + for (Object m: toMergeUrls.toArray()) { + buildAndInsert((String) m); + } + + MergeSimilarUrls.mergeAndUpdateDb(mergedUrl, toMergeUrls, 100, URLMethods.Method.GET); + + List singleTypeInfos = SingleTypeInfoDao.instance.findAll(new BasicDBObject()); + for (SingleTypeInfo singleTypeInfo: singleTypeInfos) { + assertEquals(mergedUrl, singleTypeInfo.getUrl()); + } + assertEquals(12, singleTypeInfos.size()); + + List apiInfoList = ApiInfoDao.instance.findAll(new BasicDBObject()); + assertEquals(1, apiInfoList.size()); + assertEquals(mergedUrl, apiInfoList.get(0).getId().url); + + List sampleDataList = SampleDataDao.instance.findAll(new BasicDBObject()); + assertEquals(1, sampleDataList.size()); + assertEquals(mergedUrl, sampleDataList.get(0).getId().url); + + long trafficInfoCount = TrafficInfoDao.instance.getMCollection().countDocuments(); + assertEquals(0, trafficInfoCount); + + List sensitiveSampleDataList = SensitiveSampleDataDao.instance.findAll(new BasicDBObject()); + for (SensitiveSampleData sensitiveSampleData: sensitiveSampleDataList) { + assertEquals(mergedUrl, sensitiveSampleData.getId().getUrl()); + } + assertEquals(12, sensitiveSampleDataList.size()); + + List sensitiveParamInfoList = SensitiveParamInfoDao.instance.findAll(new BasicDBObject()); + for (SensitiveParamInfo sensitiveParamInfo: sensitiveParamInfoList) { + assertEquals(mergedUrl, sensitiveParamInfo.getUrl()); + } + assertEquals(12, sensitiveParamInfoList.size()); + + List filterSampleDataList = FilterSampleDataDao.instance.findAll(new BasicDBObject()); + for (FilterSampleData filterSampleData: filterSampleDataList) { + assertEquals(mergedUrl, filterSampleData.getId().getApiInfoKey().getUrl()); + } + assertEquals(3, filterSampleDataList.size()); + } + + public void buildAndInsert(String url) { + List params = new ArrayList<>(); + for (int i = 0; i<4; i++) { + params.add("param_" + url + "_"+i); + } + int apiCollectionId = 100; + URLMethods.Method method = URLMethods.Method.GET; + List singleTypeInfos = new ArrayList<>(); + for (String param: params) { + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId(url, method.name(), 200, false, param, SingleTypeInfo.GENERIC, apiCollectionId, false); + SingleTypeInfo sti = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 0, 0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + singleTypeInfos.add(sti); + } + SingleTypeInfoDao.instance.insertMany(singleTypeInfos); + + ApiInfo apiInfo = new ApiInfo(apiCollectionId, url, method); + ApiInfoDao.instance.insertOne(apiInfo); + + SampleDataDao.instance.insertOne( + new SampleData( + new Key(apiCollectionId, url, method, -1, 0,0), + Collections.singletonList("") + ) + ); + + List trafficInfoList = new ArrayList<>(); + for (int i =0; i<10; i++) { + Key key = new Key(apiCollectionId, url, method, -1, i*10,i*10+10); + TrafficInfo trafficInfo = new TrafficInfo(key, new HashMap<>()); + trafficInfoList.add(trafficInfo); + } + + TrafficInfoDao.instance.insertMany(trafficInfoList); + + + List sensitiveSampleDataList = new ArrayList<>(); + for (String param: params) { + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId(url, method.name(), 200, false, param, SingleTypeInfo.GENERIC, apiCollectionId, false); + SensitiveSampleData sensitiveSampleData = new SensitiveSampleData( + paramId, Collections.singletonList("1") + ); + sensitiveSampleDataList.add(sensitiveSampleData); + } + SensitiveSampleDataDao.instance.insertMany(sensitiveSampleDataList); + + + List sensitiveParamInfos = new ArrayList<>(); + for (String param: params) { + SensitiveParamInfo sensitiveParamInfo = new SensitiveParamInfo( + url, method.name(), 200, false, param, apiCollectionId, true + ); + + sensitiveParamInfos.add(sensitiveParamInfo); + } + SensitiveParamInfoDao.instance.insertMany(sensitiveParamInfos); + + List filterSampleDataList = new ArrayList<>(); + for (int i =0;i<3; i++) { + FilterSampleData filterSampleData = new FilterSampleData(apiInfo.getId(), i); + filterSampleDataList.add(filterSampleData); + } + FilterSampleDataDao.instance.insertMany(filterSampleDataList); + + + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/TestMainSubFunctions.java b/apps/api-runtime/src/test/java/com/akto/runtime/TestMainSubFunctions.java new file mode 100644 index 0000000000..00611a2cfd --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/TestMainSubFunctions.java @@ -0,0 +1,97 @@ +package com.akto.runtime; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.*; + +import com.akto.MongoBasedTest; +import com.akto.dao.AccountSettingsDao; +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.context.Context; +import com.akto.dto.AccountSettings; +import com.akto.dto.ApiCollection; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; + +import org.junit.Before; +import org.junit.Test; + +public class TestMainSubFunctions extends MongoBasedTest{ + private static int currAccountId = 0; + ObjectMapper mapper = new ObjectMapper(); + + @Before + public void changeAccountId() { + Context.accountId.set(currAccountId); + currAccountId += 1; + } + + @Test + public void testTryForCollectionNameWithoutCidr() throws JsonProcessingException { + int vxlan_id = 10000; + String group_name = "bb"; + + ApiCollectionsDao.instance.insertOne(new ApiCollection( + vxlan_id, null, 0, new HashSet<>(), null, vxlan_id + )); + + Map m = new HashMap<>(); + m.put(Main.GROUP_NAME, group_name); + m.put(Main.VXLAN_ID, vxlan_id); + + String message = mapper.writeValueAsString(m); + Main.tryForCollectionName(message); + + ApiCollection apiCollection = ApiCollectionsDao.instance.findOne(Filters.eq(ApiCollection.VXLAN_ID,vxlan_id )); + assertEquals(apiCollection.getVxlanId(), vxlan_id); + assertEquals(apiCollection.getName(), group_name); + assertNull(apiCollection.getHostName()); + + AccountSettings accountSettings = AccountSettingsDao.instance.findOne(Filters.eq("_id", Context.accountId.get())); + assertNull(accountSettings); + } + + + @Test + public void testTryForCollectionNameWithCidr() throws JsonProcessingException { + int vxlan_id = 10000; + String group_name = "bb"; + List vpc_cidr = Arrays.asList("192.1.1.1/16", "193.1.1.1/16"); + + ApiCollectionsDao.instance.insertOne(new ApiCollection( + vxlan_id, null, 0, new HashSet<>(), null, vxlan_id + )); + + Map m = new HashMap<>(); + m.put(Main.GROUP_NAME, group_name); + m.put(Main.VXLAN_ID, vxlan_id); + m.put(Main.VPC_CIDR, vpc_cidr); + + String message = mapper.writeValueAsString(m); + Main.tryForCollectionName(message); + + ApiCollection apiCollection = ApiCollectionsDao.instance.findOne(Filters.eq(ApiCollection.VXLAN_ID,vxlan_id )); + assertEquals(apiCollection.getVxlanId(), vxlan_id); + assertEquals(apiCollection.getName(), group_name); + assertNull(apiCollection.getHostName()); + + AccountSettings accountSettings = AccountSettingsDao.instance.findOne(Filters.eq("_id", Context.accountId.get())); + assertNotNull(accountSettings); + assertEquals(accountSettings.getId(), Context.accountId.get()); + assertEquals(accountSettings.getPrivateCidrList().size(), 2); + assertTrue(accountSettings.getPrivateCidrList().containsAll(vpc_cidr)); + + Main.tryForCollectionName(message); + List accountSettingsList = AccountSettingsDao.instance.findAll(new BasicDBObject()); + assertEquals(accountSettingsList.size(), 1); + assertEquals(accountSettingsList.get(0).getPrivateCidrList().size(), 2); + + List apiCollectionList = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + assertEquals(apiCollectionList.size(), 1); + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/merge/TestMergeOnHostOnly.java b/apps/api-runtime/src/test/java/com/akto/runtime/merge/TestMergeOnHostOnly.java new file mode 100644 index 0000000000..121e6baaa3 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/merge/TestMergeOnHostOnly.java @@ -0,0 +1,167 @@ +package com.akto.runtime.merge; + +import org.junit.Test; + +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.ApiInfoDao; +import com.akto.dao.SampleDataDao; +import com.akto.dao.SensitiveSampleDataDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.TrafficInfoDao; +import com.akto.dto.ApiCollection; +import com.akto.dto.ApiInfo; +import com.akto.dto.SensitiveSampleData; +import com.akto.dto.ApiInfo.ApiInfoKey; +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.traffic.TrafficInfo; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods.Method; +import com.akto.types.CappedSet; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.InsertOneModel; +import com.mongodb.client.model.WriteModel; + +import com.akto.MongoBasedTest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.*; + +public class TestMergeOnHostOnly extends MongoBasedTest { + + private WriteModel createSingleTypeInfoUpdate(String url, String method, SingleTypeInfo.SubType subType, int apiCollectionId, int responseCode) { + SingleTypeInfoDao.instance.getMCollection().drop(); + CappedSet values = new CappedSet<>(); + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + url, method,responseCode, false, "param", subType, apiCollectionId, false + ); + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 100,0,0, values, SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + return new InsertOneModel<>(singleTypeInfo); + } + + private WriteModel createSingleTypeInfoHostUpdate(int apiCollectionId, String url, String method) { + SingleTypeInfoDao.instance.getMCollection().drop(); + CappedSet values = new CappedSet<>(); + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + url, method,-1,true, "host", SingleTypeInfo.GENERIC, apiCollectionId, false + ); + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 100,0,0, values, SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + return new InsertOneModel<>(singleTypeInfo); + } + + @Test + public void test1(){ + ApiCollectionsDao.instance.getMCollection().drop(); + ApiInfoDao.instance.getMCollection().drop(); + SampleDataDao.instance.getMCollection().drop(); + SensitiveSampleDataDao.instance.getMCollection().drop(); + TrafficInfoDao.instance.getMCollection().drop(); + SingleTypeInfoDao.instance.getMCollection().drop(); + + Set url1 = new HashSet<>(); + Set url2 = new HashSet<>(); + ApiCollection ap1 = new ApiCollection(1, "something-red", 0, url1, "http://www.akto.io", 1); + ApiCollection ap2 = new ApiCollection(2, "something-blue", 0, url2, "http://www.akto.io", 2); + + ApiCollection ap3 = new ApiCollection(3, "something-red", 0, url1, "http://www.notakto.io", 1); + ApiCollection ap4 = new ApiCollection(4, "something-blue", 0, url2, "http://www.notakto.io", 2); + ApiCollection ap5 = new ApiCollection(5, "something-yellow", 0, url2, "http://www.notakto.io", 2); + ApiCollectionsDao.instance.getMCollection().insertOne(ap1); + ApiCollectionsDao.instance.getMCollection().insertOne(ap2); + ApiCollectionsDao.instance.getMCollection().insertOne(ap3); + ApiCollectionsDao.instance.getMCollection().insertOne(ap4); + ApiCollectionsDao.instance.getMCollection().insertOne(ap5); + + List> bulkWrites = new ArrayList<>(); + bulkWrites.add(createSingleTypeInfoUpdate("/api/book", "GET", SingleTypeInfo.EMAIL, 1,200)); + bulkWrites.add(createSingleTypeInfoUpdate("/api/book", "GET", SingleTypeInfo.EMAIL, 1,-1)); + bulkWrites.add(createSingleTypeInfoUpdate("/api/duck", "GET", SingleTypeInfo.EMAIL, 1,200)); + bulkWrites.add(createSingleTypeInfoUpdate("/api/cat", "GET", SingleTypeInfo.EMAIL, 1,200)); + bulkWrites.add(createSingleTypeInfoUpdate("/api/car", "POST", SingleTypeInfo.EMAIL, 2,200)); + bulkWrites.add(createSingleTypeInfoUpdate("/api/duck", "GET", SingleTypeInfo.EMAIL, 2,200)); + bulkWrites.add(createSingleTypeInfoUpdate("/api/carrot", "GET", SingleTypeInfo.EMAIL, 2,200)); + bulkWrites.add(createSingleTypeInfoUpdate("/api/book", "GET", SingleTypeInfo.EMAIL, 2,200)); + + bulkWrites.add(createSingleTypeInfoHostUpdate(1, "/api/book", "GET")); + bulkWrites.add(createSingleTypeInfoHostUpdate(1, "/api/duck", "GET")); + bulkWrites.add(createSingleTypeInfoHostUpdate(1, "/api/cat", "GET")); + bulkWrites.add(createSingleTypeInfoHostUpdate(2, "/api/car", "POST")); + bulkWrites.add(createSingleTypeInfoHostUpdate(2, "/api/duck", "GET")); + bulkWrites.add(createSingleTypeInfoHostUpdate(2, "/api/carrot", "GET")); + bulkWrites.add(createSingleTypeInfoHostUpdate(2, "/api/book", "GET")); + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkWrites); + + List apiInfos = new ArrayList<>(); + apiInfos.add( new ApiInfo(new ApiInfoKey(1, "/api/book", Method.GET))); + apiInfos.add( new ApiInfo(new ApiInfoKey(1, "/api/duck", Method.GET))); + apiInfos.add( new ApiInfo(new ApiInfoKey(1, "/api/cat", Method.GET))); + apiInfos.add( new ApiInfo(new ApiInfoKey(2, "/api/car", Method.POST))); + apiInfos.add( new ApiInfo(new ApiInfoKey(2, "/api/duck", Method.GET))); + apiInfos.add( new ApiInfo(new ApiInfoKey(2, "/api/carrot", Method.GET))); + apiInfos.add( new ApiInfo(new ApiInfoKey(2, "/api/book", Method.GET))); + ApiInfoDao.instance.insertMany(apiInfos); + + List sampleDatas = new ArrayList<>(); + sampleDatas.add(new SampleData(new Key(1, "/api/book", Method.GET, 200, 0,0), Collections.singletonList(""))); + sampleDatas.add(new SampleData(new Key(1, "/api/duck", Method.GET, 200, 0,0), Collections.singletonList(""))); + sampleDatas.add(new SampleData(new Key(1, "/api/cat", Method.GET, 200, 0,0), Collections.singletonList(""))); + sampleDatas.add(new SampleData(new Key(2, "/api/car", Method.POST, 200, 0,0), Collections.singletonList(""))); + sampleDatas.add(new SampleData(new Key(2, "/api/duck", Method.GET, 200, 0,0), Collections.singletonList(""))); + sampleDatas.add(new SampleData(new Key(2, "/api/carrot", Method.GET, 200, 0,0), Collections.singletonList(""))); + sampleDatas.add(new SampleData(new Key(2, "/api/book", Method.GET, 200, 0,0), Collections.singletonList(""))); + SampleDataDao.instance.insertMany(sampleDatas); + + List sensitiveSampleDatas = new ArrayList<>(); + sensitiveSampleDatas.add(new SensitiveSampleData(new SingleTypeInfo.ParamId("/api/book", "GET",200, false, "param", SingleTypeInfo.EMAIL, 1, false), Collections.singletonList(""))); + sensitiveSampleDatas.add(new SensitiveSampleData(new SingleTypeInfo.ParamId("/api/duck", "GET",200, false, "param", SingleTypeInfo.EMAIL, 1, false), Collections.singletonList(""))); + sensitiveSampleDatas.add(new SensitiveSampleData(new SingleTypeInfo.ParamId("/api/cat", "GET",200, false, "param", SingleTypeInfo.EMAIL, 1, false), Collections.singletonList(""))); + sensitiveSampleDatas.add(new SensitiveSampleData(new SingleTypeInfo.ParamId("/api/car", "POST",200, false, "param", SingleTypeInfo.EMAIL, 2, false), Collections.singletonList(""))); + sensitiveSampleDatas.add(new SensitiveSampleData(new SingleTypeInfo.ParamId("/api/duck", "GET",200, false, "param", SingleTypeInfo.EMAIL, 2, false), Collections.singletonList(""))); + sensitiveSampleDatas.add(new SensitiveSampleData(new SingleTypeInfo.ParamId("/api/carrot", "GET",200, false, "param", SingleTypeInfo.EMAIL, 2, false), Collections.singletonList(""))); + sensitiveSampleDatas.add(new SensitiveSampleData(new SingleTypeInfo.ParamId("/api/book", "GET",200, false, "param", SingleTypeInfo.EMAIL, 2, false), Collections.singletonList(""))); + SensitiveSampleDataDao.instance.insertMany(sensitiveSampleDatas); + + List trafficInfos = new ArrayList<>(); + trafficInfos.add(new TrafficInfo(new Key(1, "/api/book", Method.GET, 200, 0,0),new HashMap<>())); + trafficInfos.add(new TrafficInfo(new Key(1, "/api/duck", Method.GET, 200, 0,0),new HashMap<>())); + trafficInfos.add(new TrafficInfo(new Key(1, "/api/cat", Method.GET, 200, 0,0),new HashMap<>())); + trafficInfos.add(new TrafficInfo(new Key(2, "/api/car", Method.POST, 200, 0,0),new HashMap<>())); + trafficInfos.add(new TrafficInfo(new Key(2, "/api/duck", Method.GET, 200, 0,0),new HashMap<>())); + trafficInfos.add(new TrafficInfo(new Key(2, "/api/carrot", Method.GET, 200, 0,0),new HashMap<>())); + trafficInfos.add(new TrafficInfo(new Key(2, "/api/book", Method.GET, 200, 0,0),new HashMap<>())); + TrafficInfoDao.instance.insertMany(trafficInfos); + + MergeOnHostOnly me = new MergeOnHostOnly(); + me.mergeHosts(); + + List newCollections = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + assertEquals(2, newCollections.size()); + int newId = newCollections.get(1).getId(); + + List si = SingleTypeInfoDao.instance.findAll("apiCollectionId",newId); + List siAll = SingleTypeInfoDao.instance.findAll(new BasicDBObject()); + assertEquals(11,si.size()); + assertEquals(11,siAll.size()); + + List ai = ApiInfoDao.instance.findAll("_id.apiCollectionId",newId); + List aiAll = ApiInfoDao.instance.findAll(new BasicDBObject()); + assertEquals(5,ai.size()); + assertEquals(5,aiAll.size()); + + List sa = SampleDataDao.instance.findAll("_id.apiCollectionId",newId); + List saAll = SampleDataDao.instance.findAll(new BasicDBObject()); + assertEquals(5,sa.size()); + assertEquals(5,saAll.size()); + + List se = SensitiveSampleDataDao.instance.findAll("_id.apiCollectionId",newId); + List seAll = SensitiveSampleDataDao.instance.findAll(new BasicDBObject()); + assertEquals(5,se.size()); + assertEquals(5,seAll.size()); + + List tr = TrafficInfoDao.instance.findAll("_id.apiCollectionId",newId); + List trAll = TrafficInfoDao.instance.findAll(new BasicDBObject()); + assertEquals(5,tr.size()); + assertEquals(5,trAll.size()); + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/policies/ApiAccessTypePolicyTest.java b/apps/api-runtime/src/test/java/com/akto/runtime/policies/ApiAccessTypePolicyTest.java new file mode 100644 index 0000000000..510a7cc198 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/policies/ApiAccessTypePolicyTest.java @@ -0,0 +1,74 @@ +package com.akto.runtime.policies; + +import com.akto.dto.ApiInfo; +import com.akto.dto.HttpRequestParams; +import com.akto.dto.HttpResponseParams; +import org.junit.jupiter.api.Assertions; +import org.junit.Test; + +import java.util.*; + +public class ApiAccessTypePolicyTest { + ApiAccessTypePolicy apiAccessTypePolicy = new ApiAccessTypePolicy(Collections.singletonList("172.31.0.0/16")); + + public static HttpResponseParams generateHttpResponseParams(List ipList) { + Map> headers = new HashMap<>(); + headers.put(ApiAccessTypePolicy.X_FORWARDED_FOR, ipList); + HttpRequestParams httpRequestParams = new HttpRequestParams("GET", "/a", "", headers, "", 0); + return new HttpResponseParams("",200,"",new HashMap<>(),"",httpRequestParams ,0,"0",false, HttpResponseParams.Source.OTHER, "", ""); + } + + @Test + public void testPublic() { + List ipList = Arrays.asList("3.109.56.64", "118.185.162.194"); + HttpResponseParams httpResponseParams = generateHttpResponseParams(ipList); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = apiAccessTypePolicy.findApiAccessType(httpResponseParams,apiInfo, null); + Assertions.assertFalse(result); + Assertions.assertEquals(apiInfo.getApiAccessTypes().size(),1); + Assertions.assertTrue(apiInfo.getApiAccessTypes().contains(ApiInfo.ApiAccessType.PUBLIC)); + } + + @Test + public void testPublicAlreadyPrivate() { + List ipList = Collections.singletonList("3.109.56.64"); + HttpResponseParams httpResponseParams = generateHttpResponseParams(ipList); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + apiInfo.getApiAccessTypes().add(ApiInfo.ApiAccessType.PRIVATE); + boolean result = apiAccessTypePolicy.findApiAccessType(httpResponseParams,apiInfo, null); + Assertions.assertFalse(result); + Assertions.assertEquals(apiInfo.getApiAccessTypes().size(), 2); + } + + @Test + public void testPrivateAlreadyPublic() { + List ipList = Arrays.asList("172.31.8.188", "172.31.255.255"); + HttpResponseParams httpResponseParams = generateHttpResponseParams(ipList); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + apiInfo.getApiAccessTypes().add(ApiInfo.ApiAccessType.PUBLIC); + boolean result = apiAccessTypePolicy.findApiAccessType(httpResponseParams,apiInfo, null); + Assertions.assertFalse(result); + Assertions.assertEquals(apiInfo.getApiAccessTypes().size(), 2); + } + + @Test + public void testPrivate() { + List ipList = Arrays.asList("172.31.8.188", "172.31.255.255"); + HttpResponseParams httpResponseParams = generateHttpResponseParams(ipList); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = apiAccessTypePolicy.findApiAccessType(httpResponseParams,apiInfo, null); + Assertions.assertFalse(result); + Assertions.assertTrue(apiInfo.getApiAccessTypes().contains(ApiInfo.ApiAccessType.PRIVATE)); + } + + @Test + public void testPublicAndPrivateMultiple() { + List ipList = Arrays.asList("172.31.255.255", "118.185.162.194"); + HttpResponseParams httpResponseParams = generateHttpResponseParams(ipList); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = apiAccessTypePolicy.findApiAccessType(httpResponseParams,apiInfo, null); + Assertions.assertFalse(result); + Assertions.assertEquals(apiInfo.getApiAccessTypes().size(), 1); + Assertions.assertTrue(apiInfo.getApiAccessTypes().contains(ApiInfo.ApiAccessType.PUBLIC)); + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/policies/AuthPolicyTest.java b/apps/api-runtime/src/test/java/com/akto/runtime/policies/AuthPolicyTest.java new file mode 100644 index 0000000000..907ac9da34 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/policies/AuthPolicyTest.java @@ -0,0 +1,277 @@ +package com.akto.runtime.policies; + +import com.akto.MongoBasedTest; +import com.akto.dao.CustomAuthTypeDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.CustomAuthType; +import com.akto.dto.HttpRequestParams; +import com.akto.dto.HttpResponseParams; +import com.mongodb.BasicDBObject; + +import org.junit.jupiter.api.Assertions; +import org.junit.Test; + +import java.util.*; + + +public class AuthPolicyTest extends MongoBasedTest { + + public static HttpResponseParams generateHttpResponseParams(Map> headers) { + HttpRequestParams httpRequestParams = new HttpRequestParams("GET", "/a", "", headers, "", 0); + return new HttpResponseParams("",200,"",new HashMap<>(),"",httpRequestParams ,0,"0",false, HttpResponseParams.Source.OTHER, "", ""); + } + + public static String createSimpleResponsePayload() { + BasicDBObject ret = new BasicDBObject(); + ret.append("a1", 1).append("a2", "2").append("a3", 3).append("AT", "somerandomlygeneratedtoken"); + return ret.toJson(); + } + + List customAuthTypes = new ArrayList<>(); + + @Test + public void testUnauthenticated() { + Map> headers = new HashMap<>(); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertTrue(result); + } + + @Test + public void testUnauthenticatedWithData() { + Map> headers = new HashMap<>(); + headers.put(AuthPolicy.AUTHORIZATION_HEADER_NAME, Collections.singletonList("Bearer woiefjwoeifw w")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.AUTHORIZATION_HEADER); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testAuthHeader() { + Map> headers = new HashMap<>(); + headers.put("auth", Collections.singletonList("Bearer woiefjwoeifw something somethingElse")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.AUTHORIZATION_HEADER); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testBearer() { + Map> headers = new HashMap<>(); + headers.put(AuthPolicy.AUTHORIZATION_HEADER_NAME, Collections.singletonList("Bearer woiefjwoeifw")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.BEARER); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testBearerWithoutHeader() { + Map> headers = new HashMap<>(); + headers.put("someRandom", Collections.singletonList("bearer woiefjwoeifw")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.BEARER); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testBearerInCookie() { + Map> headers = new HashMap<>(); + headers.put("cookie", Collections.singletonList("auth=bearer woiefj")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.BEARER); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testBearerWithExistingUnauthenticated() { + Map> headers = new HashMap<>(); + headers.put(AuthPolicy.AUTHORIZATION_HEADER_NAME, Collections.singletonList("Bearer woiefjwoeifw")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.UNAUTHENTICATED); + apiInfo.getAllAuthTypesFound().add(s); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 2); + Set s2 = new HashSet<>(); + s2.add(ApiInfo.AuthType.BEARER); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s2)); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testBasic() { + Map> headers = new HashMap<>(); + headers.put(AuthPolicy.AUTHORIZATION_HEADER_NAME, Collections.singletonList("Basic woiefjwoeifw")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.BASIC); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testBasicWithoutHeader() { + Map> headers = new HashMap<>(); + headers.put("someRandom", Collections.singletonList("basic woiefjwoeifw")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.BASIC); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testBasicInCookie() { + Map> headers = new HashMap<>(); + headers.put("cookie", Collections.singletonList("auth=Basic woiefjwoeifw")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.BASIC); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testJwt() { + Map> headers = new HashMap<>(); + headers.put("someRandom", Collections.singletonList("eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjQwNjkzNDUzLCJleHAiOjE2NDEyMTE4NTN9.oTq5FEeTlNt1YjaZ9JA8qdymArxJ8unNI8m5HLYn4ECeFOKQCv8SWnQ6uvwbbWPHa6HOYeLoD-tvPyVq-c6jlyGNf7bno8cCMB5ldyJ-I--F1xVp0iWKCMtlgdS2DgwFBdaZ9mdLCP3eZuieQV2Za8Lrzw1G1CpgJ-3vkijTw3KurKSDLT5Zv8JQRSxwj_VLeuaVkhSjYVltzTfY5tkl3CO3vNmlz6HIc4shxFXowA30xxgL438V1ELamv85fyGXg2EMhk5XeRDXq1QiLPBsQZ28FSk5TJAn2Xc_pwWXBw-N2P6Y_Hh0bL7KXpErgKQNQiAfNFHFzAUbuLefD6dJKg")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.JWT); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testJwtInCookie() { + Map> headers = new HashMap<>(); + headers.put("cookie", Collections.singletonList("Path=/; JWT=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjQwNjkzNDUzLCJleHAiOjE2NDEyMTE4NTN9.oTq5FEeTlNt1YjaZ9JA8qdymArxJ8unNI8m5HLYn4ECeFOKQCv8SWnQ6uvwbbWPHa6HOYeLoD-tvPyVq-c6jlyGNf7bno8cCMB5ldyJ-I--F1xVp0iWKCMtlgdS2DgwFBdaZ9mdLCP3eZuieQV2Za8Lrzw1G1CpgJ-3vkijTw3KurKSDLT5Zv8JQRSxwj_VLeuaVkhSjYVltzTfY5tkl3CO3vNmlz6HIc4shxFXowA30xxgL438V1ELamv85fyGXg2EMhk5XeRDXq1QiLPBsQZ28FSk5TJAn2Xc_pwWXBw-N2P6Y_Hh0bL7KXpErgKQNQiAfNFHFzAUbuLefD6dJKg; HttpOnly")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.JWT); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testMultipleHappy() { + Map> headers = new HashMap<>(); + headers.put("someRandom", Collections.singletonList("eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjQwNjkzNDUzLCJleHAiOjE2NDEyMTE4NTN9.oTq5FEeTlNt1YjaZ9JA8qdymArxJ8unNI8m5HLYn4ECeFOKQCv8SWnQ6uvwbbWPHa6HOYeLoD-tvPyVq-c6jlyGNf7bno8cCMB5ldyJ-I--F1xVp0iWKCMtlgdS2DgwFBdaZ9mdLCP3eZuieQV2Za8Lrzw1G1CpgJ-3vkijTw3KurKSDLT5Zv8JQRSxwj_VLeuaVkhSjYVltzTfY5tkl3CO3vNmlz6HIc4shxFXowA30xxgL438V1ELamv85fyGXg2EMhk5XeRDXq1QiLPBsQZ28FSk5TJAn2Xc_pwWXBw-N2P6Y_Hh0bL7KXpErgKQNQiAfNFHFzAUbuLefD6dJKg")); + headers.put(AuthPolicy.AUTHORIZATION_HEADER_NAME, Collections.singletonList("Basic woiefjwoeifw")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.JWT); + s.add(ApiInfo.AuthType.BASIC); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + @Test + public void testMultipleWithExistingData() { + Map> headers = new HashMap<>(); + headers.put("someRandom", Collections.singletonList("eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmt1c2hAZ21haWwuY29tIiwiaWF0IjoxNjQwNjkzNDUzLCJleHAiOjE2NDEyMTE4NTN9.oTq5FEeTlNt1YjaZ9JA8qdymArxJ8unNI8m5HLYn4ECeFOKQCv8SWnQ6uvwbbWPHa6HOYeLoD-tvPyVq-c6jlyGNf7bno8cCMB5ldyJ-I--F1xVp0iWKCMtlgdS2DgwFBdaZ9mdLCP3eZuieQV2Za8Lrzw1G1CpgJ-3vkijTw3KurKSDLT5Zv8JQRSxwj_VLeuaVkhSjYVltzTfY5tkl3CO3vNmlz6HIc4shxFXowA30xxgL438V1ELamv85fyGXg2EMhk5XeRDXq1QiLPBsQZ28FSk5TJAn2Xc_pwWXBw-N2P6Y_Hh0bL7KXpErgKQNQiAfNFHFzAUbuLefD6dJKg")); + headers.put(AuthPolicy.AUTHORIZATION_HEADER_NAME, Collections.singletonList("Basic woiefjwoeifw")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.JWT); + s.add(ApiInfo.AuthType.BASIC); + apiInfo.getAllAuthTypesFound().add(s); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,customAuthTypes); + Assertions.assertFalse(result); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testCustomAuthTypeHeader() { + Map> headers = new HashMap<>(); + headers.put("AT", Collections.singletonList("arandomlygeneratedsecurecode")); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + List keys = new ArrayList<>(); + keys.add("AT"); + CustomAuthType customAuthType = new CustomAuthType("AT", keys,keys, true,0); + CustomAuthTypeDao.instance.insertOne(customAuthType); + List authTypes = new ArrayList<>(Collections.singletonList(customAuthType)); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,authTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.CUSTOM); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } + + @Test + public void testCustomAuthTypePayload() { + Map> headers = new HashMap<>(); + HttpResponseParams httpResponseParams = generateHttpResponseParams(headers); + + BasicDBObject ret = new BasicDBObject(); + BasicDBObject nested = new BasicDBObject(); + nested.append("AT", "somerandomlygeneratedtoken"); + ret.append("a1", 1).append("a2", "2").append("a3", nested); + String payload = ret.toJson(); + + httpResponseParams.getRequestParams().setPayload(payload); + + List keys = new ArrayList<>(); + keys.add("a3.AT"); + CustomAuthType customAuthType = new CustomAuthType("AT", new ArrayList<>(),keys, true,0); + + CustomAuthTypeDao.instance.insertOne(customAuthType); + List authTypes = new ArrayList<>(Collections.singletonList(customAuthType)); + ApiInfo apiInfo = new ApiInfo(httpResponseParams); + boolean result = AuthPolicy.findAuthType(httpResponseParams,apiInfo, null,authTypes); + Assertions.assertFalse(result); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.CUSTOM); + Assertions.assertEquals(apiInfo.getAllAuthTypesFound().size(), 1); + Assertions.assertTrue(apiInfo.getAllAuthTypesFound().contains(s)); + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/policies/SetFieldPolicyTest.java b/apps/api-runtime/src/test/java/com/akto/runtime/policies/SetFieldPolicyTest.java new file mode 100644 index 0000000000..fb7b84b099 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/policies/SetFieldPolicyTest.java @@ -0,0 +1,32 @@ +package com.akto.runtime.policies; + +import com.akto.dto.ApiInfo; +import com.akto.dto.runtime_filters.RuntimeFilter; +import com.akto.dto.type.URLMethods.Method; + +import org.junit.jupiter.api.Assertions; +import org.junit.Test; + +public class SetFieldPolicyTest { + + @Test + public void happy() { + ApiInfo apiInfo = new ApiInfo(); + RuntimeFilter runtimeFilter = new RuntimeFilter(); + runtimeFilter.setCustomFieldName("field"); + SetFieldPolicy.setField(null,apiInfo, runtimeFilter); + Assertions.assertEquals(apiInfo.getViolations().size(), 1); + Assertions.assertNotNull(apiInfo.getViolations().get("field")); + } + + @Test + public void happyExisting() { + ApiInfo apiInfo = new ApiInfo(0,"",Method.DELETE); + apiInfo.getViolations().put("field", 0); + RuntimeFilter runtimeFilter = new RuntimeFilter(); + runtimeFilter.setCustomFieldName("field"); + SetFieldPolicy.setField(null,apiInfo, runtimeFilter); + Assertions.assertEquals(apiInfo.getViolations().size(), 1); + Assertions.assertNotNull(apiInfo.getViolations().get("field")); + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/policies/TestAktoPolicy.java b/apps/api-runtime/src/test/java/com/akto/runtime/policies/TestAktoPolicy.java new file mode 100644 index 0000000000..f993620187 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/policies/TestAktoPolicy.java @@ -0,0 +1,286 @@ +package com.akto.runtime.policies; + +import com.akto.MongoBasedTest; +import com.akto.dao.*; +import com.akto.dao.context.Context; +import com.akto.dto.*; +import com.akto.dto.type.*; +import com.akto.parsers.HttpCallParser; +import com.akto.runtime.APICatalogSync; +import com.akto.runtime.merge.MergeSimilarUrls; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import java.util.*; + +import static org.junit.Assert.*; + +public class TestAktoPolicy extends MongoBasedTest { + private static int currAccountId = 0; + + @Before + public void changeAccountId() { + Context.accountId.set(currAccountId); + currAccountId += 1; + } + + public HttpResponseParams generateHttpResponseParams(String url, URLMethods.Method method, int aci, + List authTypes, boolean isPublic) { + HttpResponseParams hrp = new HttpResponseParams(); + hrp.requestParams = new HttpRequestParams(); + hrp.requestParams.setApiCollectionId(aci); + hrp.requestParams.url = url; + hrp.requestParams.method = method.name(); + hrp.statusCode = 200; + hrp.requestParams.setHeaders(new HashMap<>()); + hrp.requestParams.getHeaders().put("content-type", Collections.singletonList("application/json")); + hrp.setOrig("BBB"); + + // authType + for (ApiInfo.AuthType authType: authTypes) { + switch (authType) { + case UNAUTHENTICATED: + break; + case BASIC: + hrp.requestParams.getHeaders().put("authorization", Collections.singletonList("Basic somerandom")); + break; + case AUTHORIZATION_HEADER: + hrp.requestParams.getHeaders().put("authorization", Collections.singletonList("somerandom")); + break; + case JWT: + hrp.requestParams.getHeaders().put("akto-token123", Collections.singletonList("eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g")); + break; + case API_TOKEN: + break; + case BEARER: + hrp.requestParams.getHeaders().put("authorization", Collections.singletonList("Bearer somerandom")); + break; + } + } + + // public/private + if (isPublic) { + hrp.requestParams.getHeaders().put(ApiAccessTypePolicy.X_FORWARDED_FOR, Arrays.asList("3.109.56.64", "118.185.162.194")); + } else { + hrp.requestParams.getHeaders().put(ApiAccessTypePolicy.X_FORWARDED_FOR, Arrays.asList("172.31.8.188", "172.31.255.255")); + } + return hrp; + } + + @Test + public void testRestart() throws Exception { + dropCollections(); + initialiseAccountSettings(); + + HttpResponseParams hrp1 = generateHttpResponseParams("url1", URLMethods.Method.GET, 0,new ArrayList<>(),false) ; + HttpResponseParams hrp2 = generateHttpResponseParams("url2", URLMethods.Method.GET, 0,new ArrayList<>(),false) ; + HttpResponseParams hrp3 = generateHttpResponseParams("url3", URLMethods.Method.GET, 0,new ArrayList<>(),false) ; + + HttpResponseParams hrpMerge1 = generateHttpResponseParams("/api/books/1", URLMethods.Method.GET, 0,new ArrayList<>(),false) ; + HttpResponseParams hrpMerge2 = generateHttpResponseParams("/api/books/2", URLMethods.Method.GET, 0,new ArrayList<>(),false) ; + + List responseParams = Arrays.asList(hrp1, hrp2, hrp3, hrpMerge1, hrpMerge2); + + HttpCallParser httpCallParser = new HttpCallParser("user", 1, 1,1, true); + AktoPolicyNew aktoPolicy = new AktoPolicyNew(true); + httpCallParser.syncFunction(responseParams, false, true); + httpCallParser.apiCatalogSync.syncWithDB(false, true); + aktoPolicy.main(responseParams, httpCallParser.apiCatalogSync != null, true); + + List apiInfoList = ApiInfoDao.instance.findAll(Filters.eq("_id.apiCollectionId", 0)); + Assertions.assertEquals(5,apiInfoList.size()); + List filterSampleDataList = FilterSampleDataDao.instance.findAll(new BasicDBObject()); + Assertions.assertEquals(5, filterSampleDataList.size()); + + // restart server means new httpCallParser and aktoPolicy + HttpCallParser httpCallParser1 = new HttpCallParser("user", 1, 1,1, true); + AktoPolicyNew aktoPolicy1 = new AktoPolicyNew( true); + + apiInfoList = ApiInfoDao.instance.findAll(Filters.eq("_id.apiCollectionId", 0)); + Assertions.assertEquals(5,apiInfoList.size()); + + httpCallParser1.syncFunction(responseParams.subList(0,1), false, true); + httpCallParser1.apiCatalogSync.syncWithDB(false, true); + aktoPolicy1.main(responseParams.subList(0,1), httpCallParser1.apiCatalogSync != null, true); + + apiInfoList = ApiInfoDao.instance.findAll(Filters.eq("_id.apiCollectionId", 0)); + Assertions.assertEquals(5,apiInfoList.size()); + filterSampleDataList = FilterSampleDataDao.instance.findAll(Filters.eq("_id.apiInfoKey.apiCollectionId", 0)); + Assertions.assertEquals(5, filterSampleDataList.size()); + } + + + @Test + public void test1() throws Exception { + dropCollections(); + initialiseAccountSettings(); + + APICatalogSync apiCatalogSync = new APICatalogSync("", 10); + AktoPolicyNew aktoPolicy = new AktoPolicyNew(true); + + URLStatic urlStatic1 = new URLStatic("/api/books", URLMethods.Method.GET); + HttpResponseParams hrp1 = generateHttpResponseParams(urlStatic1.getUrl(), urlStatic1.getMethod(),0, Collections.singletonList(ApiInfo.AuthType.JWT), true) ; + URLStatic urlStatic2 = new URLStatic("/api/books", URLMethods.Method.POST); + HttpResponseParams hrp2 = generateHttpResponseParams(urlStatic2.getUrl(), urlStatic2.getMethod(),0,Collections.singletonList(ApiInfo.AuthType.UNAUTHENTICATED), true); + URLStatic urlStatic3 = new URLStatic("/api/cars", URLMethods.Method.GET); + HttpResponseParams hrp3 = generateHttpResponseParams(urlStatic3.getUrl(), urlStatic3.getMethod(),1,Collections.singletonList(ApiInfo.AuthType.JWT), false); + URLStatic urlStatic4 = new URLStatic("/api/toys", URLMethods.Method.PUT); + HttpResponseParams hrp4 = generateHttpResponseParams(urlStatic4.getUrl(), urlStatic4.getMethod(),0,Collections.singletonList(ApiInfo.AuthType.JWT), false); + URLStatic urlStatic5 = new URLStatic("/api/new/books", URLMethods.Method.GET); + HttpResponseParams hrp5 = generateHttpResponseParams(urlStatic5.getUrl(), urlStatic5.getMethod(),0,Collections.singletonList(ApiInfo.AuthType.JWT), false); + URLStatic urlStatic6 = new URLStatic("/api/toys/1", URLMethods.Method.PUT); + HttpResponseParams hrp6 = generateHttpResponseParams(urlStatic6.getUrl(), urlStatic6.getMethod(),0,Collections.singletonList(ApiInfo.AuthType.UNAUTHENTICATED), false); + URLStatic urlStatic7 = new URLStatic("/api/toys/2", URLMethods.Method.PUT); + HttpResponseParams hrp7 = generateHttpResponseParams(urlStatic7.getUrl(), urlStatic7.getMethod(),0,Collections.singletonList(ApiInfo.AuthType.UNAUTHENTICATED), false); + + // sending a couple of requests to akto policy initially + List hrpList = Arrays.asList(hrp1, hrp2, hrp3, hrp4, hrp5, hrp6, hrp7); + aktoPolicy.main(hrpList, false, true); + + aktoPolicy.main(Collections.singletonList(hrp1), apiCatalogSync != null, true); + + List apiInfoList = ApiInfoDao.instance.findAll(new BasicDBObject()); + assertEquals(hrpList.size(), apiInfoList.size()); + + List filterSampleDataList = FilterSampleDataDao.instance.findAll(new BasicDBObject()); + assertEquals(3, filterSampleDataList.size()); + + // created a dummy AktoPolicy to use buildFromDb without touching the original AktoPolicy + AktoPolicyNew dummyAktoPolicy = new AktoPolicyNew(true); + dummyAktoPolicy.buildFromDb(true); + + Assertions.assertEquals(dummyAktoPolicy.getApiInfoCatalogMap().keySet().size(), 2); + Assertions.assertEquals(dummyAktoPolicy.getApiInfoCatalogMap().get(0).getStrictURLToMethods().size(), hrpList.size() -1 ); // because 1 hrp is of different collection + Assertions.assertEquals(dummyAktoPolicy.getApiInfoCatalogMap().get(0).getTemplateURLToMethods().size(), 0 ); + Assertions.assertEquals(dummyAktoPolicy.getApiInfoCatalogMap().get(1).getStrictURLToMethods().size(),1); + Assertions.assertEquals(dummyAktoPolicy.getApiInfoCatalogMap().get(1).getTemplateURLToMethods().size(),0); + + + // next time data comes some urls got merged + String mergedUrl = "/api/toys/INTEGER"; + Set toMergeUrls = new HashSet<>(Arrays.asList("/api/toys/1", "/api/toys/2")); + MergeSimilarUrls.mergeApiInfo(mergedUrl, toMergeUrls, 0, URLMethods.Method.PUT); + MergeSimilarUrls.mergeFilterSampleData(mergedUrl, toMergeUrls, 0, URLMethods.Method.PUT); + + aktoPolicy.buildFromDb(true); + apiInfoList = ApiInfoDao.instance.findAll(new BasicDBObject()); + assertEquals(hrpList.size() - 1, apiInfoList.size()); // 2 urls got merged to 1 + filterSampleDataList = FilterSampleDataDao.instance.findAll(new BasicDBObject()); + assertEquals(2, filterSampleDataList.size()); // 2 urls got merged to 1 + dummyAktoPolicy.buildFromDb(true); + Assertions.assertEquals(dummyAktoPolicy.getApiInfoCatalogMap().get(0).getTemplateURLToMethods().size(), 1 ); + + FilterSampleData filterSampleData = FilterSampleDataDao.instance.findOne( + Filters.and( + Filters.eq("_id.apiInfoKey.apiCollectionId", 0), + Filters.eq("_id.apiInfoKey.url", "/api/toys/INTEGER"), + Filters.eq("_id.apiInfoKey.method", urlStatic6.getMethod().name()) + ) + ); + assertEquals(1, filterSampleData.getSamples().getElements().size()); + + URLStatic urlStatic8 = new URLStatic("/api/toys/3", URLMethods.Method.PUT); + HttpResponseParams hrp8 = generateHttpResponseParams(urlStatic8.getUrl(), urlStatic8.getMethod(),0, Collections.singletonList(ApiInfo.AuthType.UNAUTHENTICATED),false) ; + aktoPolicy.main(Collections.singletonList(hrp8), apiCatalogSync != null, true); + + apiInfoList = ApiInfoDao.instance.findAll(new BasicDBObject()); + assertEquals(hrpList.size() - 1, apiInfoList.size()); // 2 urls got merged to 1 + filterSampleDataList = FilterSampleDataDao.instance.findAll(new BasicDBObject()); + assertEquals(2, filterSampleDataList.size()); // 2 urls got merged to 1 + + filterSampleData = FilterSampleDataDao.instance.findOne( + Filters.and( + Filters.eq("_id.apiInfoKey.apiCollectionId", 0), + Filters.eq("_id.apiInfoKey.url", "/api/toys/INTEGER"), + Filters.eq("_id.apiInfoKey.method", urlStatic6.getMethod().name()) + ) + ); + assertEquals(2, filterSampleData.getSamples().getElements().size()); + + HttpResponseParams hrp10 = generateHttpResponseParams(urlStatic6.getUrl(), urlStatic6.getMethod(),0,Collections.singletonList(ApiInfo.AuthType.JWT), false); + + aktoPolicy.main(Collections.singletonList(hrp10), apiCatalogSync != null, true); + + ApiInfo apiInfo = ApiInfoDao.instance.findOne( + Filters.and( + Filters.eq("_id.apiCollectionId", 0), + Filters.eq("_id.url", "/api/toys/INTEGER"), + Filters.eq("_id.method", urlStatic6.getMethod().name()) + ) + ); + + assertEquals(2, apiInfo.getAllAuthTypesFound().size()); + assertTrue(apiInfo.getAllAuthTypesFound().contains(new HashSet<>(Collections.singletonList(ApiInfo.AuthType.UNAUTHENTICATED)))); + assertTrue(apiInfo.getAllAuthTypesFound().contains(new HashSet<>(Collections.singletonList(ApiInfo.AuthType.JWT)))); + + } + + + private static void dropCollections() { + AccountSettingsDao.instance.getMCollection().drop(); + FilterSampleDataDao.instance.getMCollection().drop(); + ApiInfoDao.instance.getMCollection().drop(); + } + + private static void initialiseAccountSettings() { + AccountSettings accountSettings = new AccountSettings(0, Collections.singletonList("172.31.0.0/16"), false, AccountSettings.SetupType.STAGING); + AccountSettingsDao.instance.insertOne(accountSettings); + RuntimeFilterDao.instance.initialiseFilters(); + } + + @Test + public void testFilterSampleDataGetId() { + dropCollections(); + + FilterSampleData filterSampleData1 = new FilterSampleData(new ApiInfo.ApiInfoKey(0,"/api/books", URLMethods.Method.GET), 0); + FilterSampleData filterSampleData2 = new FilterSampleData(new ApiInfo.ApiInfoKey(0,"/api/books", URLMethods.Method.POST), 0); + for (String x: Arrays.asList("1", "2")) { + filterSampleData2.getSamples().add(x); + } + FilterSampleData filterSampleData3 = new FilterSampleData(new ApiInfo.ApiInfoKey(0,"/api/toys", URLMethods.Method.PUT), 1); + for (String x: Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")) { + filterSampleData3.getSamples().add(x); + } + + FilterSampleDataDao.instance.insertMany(Arrays.asList(filterSampleData1, filterSampleData2, filterSampleData3)); + List filterSampleDataIdList = FilterSampleDataDao.instance.getApiInfoKeys(); + + Assertions.assertEquals(3, filterSampleDataIdList.size()); + Assertions.assertNotNull(filterSampleDataIdList.get(0)); + Assertions.assertNotNull(filterSampleDataIdList.get(1)); + Assertions.assertNotNull(filterSampleDataIdList.get(2)); + } + + @Test + public void testConstructorInitialisation() { + dropCollections(); + + ApiInfo apiInfo = new ApiInfo(0, "/url1", URLMethods.Method.GET); + ApiInfoDao.instance.insertOne(apiInfo); + + FilterSampleData filterSampleData = new FilterSampleData(new ApiInfo.ApiInfoKey(0, "/url1", URLMethods.Method.GET), 0); + FilterSampleDataDao.instance.insertOne(filterSampleData); + + APICatalogSync apiCatalogSync = new APICatalogSync("", 0); + Map dbState = new HashMap<>(); + Map strictURLToMethods = new HashMap<>(); + strictURLToMethods.put(new URLStatic("/url1", URLMethods.Method.GET), new RequestTemplate()); + Map templateURLToMethods = new HashMap<>(); + dbState.put(0, new APICatalog(0, strictURLToMethods, templateURLToMethods)); + + apiCatalogSync.dbState = dbState; + + AktoPolicyNew aktoPolicy = new AktoPolicyNew( true); + Map apiInfoCatalogMap = aktoPolicy.getApiInfoCatalogMap(); + + Map strictPolicyMap = apiInfoCatalogMap.get(0).getStrictURLToMethods(); + assertEquals(strictPolicyMap.size(), 1); + assertNotNull(strictPolicyMap.get(new URLStatic("/url1", URLMethods.Method.GET))); + + Map templatePolicyCatalogMap = apiInfoCatalogMap.get(0).getTemplateURLToMethods(); + assertEquals(templatePolicyCatalogMap.size(), 0); + } +} \ No newline at end of file diff --git a/apps/api-runtime/src/test/java/com/akto/runtime/policies/TestAktoPolicyWithoutDbCall.java b/apps/api-runtime/src/test/java/com/akto/runtime/policies/TestAktoPolicyWithoutDbCall.java new file mode 100644 index 0000000000..879e0cf1fe --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/runtime/policies/TestAktoPolicyWithoutDbCall.java @@ -0,0 +1,98 @@ +package com.akto.runtime.policies; + +import com.akto.dto.ApiInfo; +import com.akto.dto.type.URLMethods; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import java.util.HashMap; +import java.util.Map; + +public class TestAktoPolicyWithoutDbCall { +// +// @Test +// public void testGetApiInfoMapKeyStrict() { +// ApiInfo.ApiInfoKey apiInfoKey = new ApiInfo.ApiInfoKey(0,"/api/books", URLMethods.Method.GET); +// Map> apiInfoMap = new HashMap<>(); +// apiInfoMap.put(AktoPolicy.STRICT, new HashMap<>()); +// apiInfoMap.put(AktoPolicy.TEMPLATE, new HashMap<>()); +// +// ApiInfo.ApiInfoKey inMapKey1 = new ApiInfo.ApiInfoKey(0,"/api/books", URLMethods.Method.POST); +// ApiInfo.ApiInfoKey inMapKey2 = new ApiInfo.ApiInfoKey(0,"/api/books", URLMethods.Method.GET); +// apiInfoMap.get(AktoPolicy.STRICT).put(inMapKey1, null); +// apiInfoMap.get(AktoPolicy.STRICT).put(inMapKey2, null); +// +// AktoPolicy aktoPolicy = new AktoPolicy(false); +// aktoPolicy.setApiInfoMap(apiInfoMap); +// ApiInfo apiInfo = aktoPolicy.getApiInfoFromMap(apiInfoKey); +// Assertions.assertEquals(apiInfo.getId(), inMapKey2); +// } +// +// @Test +// public void testGetApiInfoMapKeyStrictFail() { +// ApiInfo.ApiInfoKey apiInfoKey = new ApiInfo.ApiInfoKey(0,"/api/books", URLMethods.Method.GET); +// Map> apiInfoMap = new HashMap<>(); +// apiInfoMap.put(AktoPolicy.STRICT, new HashMap<>()); +// apiInfoMap.put(AktoPolicy.TEMPLATE, new HashMap<>()); +// +// ApiInfo.ApiInfoKey inMapKey1 = new ApiInfo.ApiInfoKey(0,"/api/books", URLMethods.Method.POST); +// ApiInfo.ApiInfoKey inMapKey2 = new ApiInfo.ApiInfoKey(1,"/api/books", URLMethods.Method.GET); +// ApiInfo.ApiInfoKey inMapKey3 = new ApiInfo.ApiInfoKey(0,"/api/books/3", URLMethods.Method.GET); +// apiInfoMap.get(AktoPolicy.STRICT).put(inMapKey1, null); +// apiInfoMap.get(AktoPolicy.STRICT).put(inMapKey2, null); +// apiInfoMap.get(AktoPolicy.STRICT).put(inMapKey3, null); +// AktoPolicy aktoPolicy = new AktoPolicy(false); +// aktoPolicy.setApiInfoMap(apiInfoMap); +// ApiInfo apiInfo = aktoPolicy.getApiInfoFromMap(apiInfoKey); +// Assertions.assertNull(apiInfo); +// } +// +// @Test +// public void testGetApiInfoMapKeyTemplateHappy() { +// ApiInfo.ApiInfoKey apiInfoKey = new ApiInfo.ApiInfoKey(0,"/api/books/3/cars/7/books", URLMethods.Method.GET); +// Map> apiInfoMap = new HashMap<>(); +// apiInfoMap.put(AktoPolicy.STRICT, new HashMap<>()); +// apiInfoMap.put(AktoPolicy.TEMPLATE, new HashMap<>()); +// +// ApiInfo.ApiInfoKey inMapKey1 = new ApiInfo.ApiInfoKey(0,"/api/books/INTEGER/cars/INTEGER/books", URLMethods.Method.GET); +// apiInfoMap.get(AktoPolicy.TEMPLATE).put(inMapKey1, null); +// AktoPolicy aktoPolicy = new AktoPolicy(false); +// aktoPolicy.setApiInfoMap(apiInfoMap); +// ApiInfo apiInfo = aktoPolicy.getApiInfoFromMap(apiInfoKey); +// Assertions.assertEquals(apiInfo.getId(), inMapKey1); +// } +// +// @Test +// public void testGetApiInfoMapKeyTemplateFail() { +// ApiInfo.ApiInfoKey apiInfoKey = new ApiInfo.ApiInfoKey(0,"/api/books/3/cars/7/books", URLMethods.Method.GET); +// Map> apiInfoMap = new HashMap<>(); +// apiInfoMap.put(AktoPolicy.STRICT, new HashMap<>()); +// apiInfoMap.put(AktoPolicy.TEMPLATE, new HashMap<>()); +// +// ApiInfo.ApiInfoKey inMapKey1 = new ApiInfo.ApiInfoKey(0,"/api/books", URLMethods.Method.POST); +// ApiInfo.ApiInfoKey inMapKey2 = new ApiInfo.ApiInfoKey(1,"/api/books/INTEGER/cars/INTEGER/books", URLMethods.Method.GET); +// ApiInfo.ApiInfoKey inMapKey3 = new ApiInfo.ApiInfoKey(1,"/api/books/INTEGER/cars/INTEGER/books", URLMethods.Method.POST); +// apiInfoMap.get(AktoPolicy.STRICT).put(inMapKey1, null); +// apiInfoMap.get(AktoPolicy.TEMPLATE).put(inMapKey2, null); +// apiInfoMap.get(AktoPolicy.TEMPLATE).put(inMapKey3, null); +// AktoPolicy aktoPolicy = new AktoPolicy(false); +// aktoPolicy.setApiInfoMap(apiInfoMap); +// ApiInfo apiInfo = aktoPolicy.getApiInfoFromMap(apiInfoKey); +// Assertions.assertNull(apiInfo); +// } +// +// @Test +// public void testGetApiInfoMapKeyTemplateWithLeadingSlashHappy() { +// ApiInfo.ApiInfoKey apiInfoKey = new ApiInfo.ApiInfoKey(0,"api/books/3/cars/7/books", URLMethods.Method.GET); +// Map> apiInfoMap = new HashMap<>(); +// apiInfoMap.put(AktoPolicy.STRICT, new HashMap<>()); +// apiInfoMap.put(AktoPolicy.TEMPLATE, new HashMap<>()); +// +// ApiInfo.ApiInfoKey inMapKey1 = new ApiInfo.ApiInfoKey(0,"/api/books/INTEGER/cars/INTEGER/books", URLMethods.Method.GET); +// apiInfoMap.get(AktoPolicy.TEMPLATE).put(inMapKey1, null); +// AktoPolicy aktoPolicy = new AktoPolicy(false); +// aktoPolicy.setApiInfoMap(apiInfoMap); +// ApiInfo apiInfo = aktoPolicy.getApiInfoFromMap(apiInfoKey); +// Assertions.assertEquals(apiInfo.getId(), inMapKey1); +// } +} diff --git a/apps/api-runtime/src/test/java/com/akto/utils/CustomAuthUtilTest.java b/apps/api-runtime/src/test/java/com/akto/utils/CustomAuthUtilTest.java new file mode 100644 index 0000000000..3e45957209 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/utils/CustomAuthUtilTest.java @@ -0,0 +1,56 @@ +package com.akto.utils; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Test; + +import com.akto.MongoBasedTest; +import com.akto.dao.ApiInfoDao; +import com.akto.dao.CustomAuthTypeDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.CustomAuthType; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods.Method; +import com.akto.types.CappedSet; + +public class CustomAuthUtilTest extends MongoBasedTest{ + + public static SingleTypeInfo generateSingleTypeInfo(String param, Boolean isHeader) { + SingleTypeInfo.ParamId p = new SingleTypeInfo.ParamId("/api","POST",200,isHeader,param,SingleTypeInfo.GENERIC,ACCOUNT_ID, false); + return new SingleTypeInfo(p,new HashSet<>(),new HashSet<>(),0,0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + } + + @Test + public void test1(){ + ApiInfo apiInfo = new ApiInfo(ACCOUNT_ID, "/api", Method.POST); + Set> authTypes = new HashSet<>(); + Set types = new HashSet<>(); + types.add(ApiInfo.AuthType.UNAUTHENTICATED); + authTypes.add(types); + apiInfo.setAllAuthTypesFound(authTypes); + List customAuthTypes = new ArrayList<>(); + List headerKeys = new ArrayList<>(); + headerKeys.add("authtoken"); + headerKeys.add("newauthtoken"); + customAuthTypes.add(new CustomAuthType("auth1", headerKeys,new ArrayList<>(), true, ACCOUNT_ID)); + List singleTypeInfos = new ArrayList<>(); + singleTypeInfos.add(generateSingleTypeInfo("authtoken",true)); + singleTypeInfos.add(generateSingleTypeInfo("cookie",true)); + singleTypeInfos.get(1).setValues(new CappedSet<>(new HashSet<>(Collections.singletonList("newauthtoken=wow; someothertoken=verysecure")))); + ApiInfoDao.instance.insertOne(apiInfo); + CustomAuthTypeDao.instance.insertMany(customAuthTypes); + SingleTypeInfoDao.instance.insertMany(singleTypeInfos); + CustomAuthUtil.customAuthTypeUtil(customAuthTypes); + apiInfo = ApiInfoDao.instance.findOne(ApiInfoDao.getFilter("/api", "POST", ACCOUNT_ID)); + Set customTypes = new HashSet<>(); + customTypes.add(ApiInfo.AuthType.CUSTOM); + assertTrue(apiInfo.getAllAuthTypesFound().contains(customTypes)); + } +} \ No newline at end of file diff --git a/apps/api-runtime/src/test/java/com/akto/utils/SampleDataToSTITest.java b/apps/api-runtime/src/test/java/com/akto/utils/SampleDataToSTITest.java new file mode 100644 index 0000000000..3226bd04dc --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/utils/SampleDataToSTITest.java @@ -0,0 +1,54 @@ +package com.akto.utils; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import org.junit.Test; + +import com.akto.MongoBasedTest; +import com.akto.dao.SampleDataDao; +import com.akto.dto.AktoDataType; +import com.akto.dto.IgnoreData; +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods; +import com.mongodb.BasicDBObject; + +public class SampleDataToSTITest extends MongoBasedTest{ + + public void testInitializer(){ + SingleTypeInfo.aktoDataTypeMap = new HashMap<>(); + SingleTypeInfo.aktoDataTypeMap.put("JWT", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("PHONE_NUMBER", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("CREDIT_CARD", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("IP_ADDRESS", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("EMAIL", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("SSN", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("UUID", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("URL", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + } + + @Test + public void test1(){ + testInitializer(); + List sampleList = new ArrayList<>(); + sampleList.add("{\"method\":\"POST\",\"requestPayload\":\"{}\",\"responsePayload\":\"{\\\"active\\\":false,\\\"createNew\\\":false,\\\"keyConditionFromUsers\\\":null,\\\"keyOperator\\\":null,\\\"name\\\":null,\\\"tagConfig\\\":null,\\\"tagConfigs\\\":{\\\"tagConfigs\\\":[],\\\"usersMap\\\":{\\\"1661879740\\\":\\\"shivansh@akto.io\\\"}}}\",\"ip\":\"null\",\"source\":\"HAR\",\"type\":\"HTTP/1.1\",\"akto_vxlan_id\":\"1661880070\",\"path\":\"http://localhost:8080/api/fetchTagConfigs\",\"requestHeaders\":\"{\\\"Cookie\\\":\\\"JSESSIONID=node0e7rms6jdk2u41w0drvjv8hkoo0.node0; mp_c403d0b00353cc31d7e33d68dc778806_mixpanel=%7B%22distinct_id%22%3A%20%22182edbc00381d9-063588c46d5c5e-26021d51-144000-182edbc0039615%22%2C%22%24device_id%22%3A%20%22182edbc00381d9-063588c46d5c5e-26021d51-144000-182edbc0039615%22%2C%22%24initial_referrer%22%3A%20%22%24direct%22%2C%22%24initial_referring_domain%22%3A%20%22%24direct%22%7D\\\",\\\"Origin\\\":\\\"http://localhost:8080\\\",\\\"Accept\\\":\\\"application/json, text/plain, */*\\\",\\\"Access-Control-Allow-Origin\\\":\\\"*\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Referer\\\":\\\"http://localhost:8080/dashboard/settings\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"localhost:8080\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"access-token\\\":\\\"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoibG9naW4iLCJzaWduZWRVcCI6InRydWUiLCJ1c2VybmFtZSI6InNoaXZhbnNoQGFrdG8uaW8iLCJpYXQiOjE2NjE4ODAwNTYsImV4cCI6MTY2MTg4MDk1Nn0.wxDbUhIfhX6i8tITykZcdztg8CZUcrBvdqbLgiZJN0Q4QkGOvhHozZ6lwgFzQe3hTOxuFOv8wxg4E_vzruLMgSRmapHGuTi57qTYFWIJNb-VSUa_Nz__t6aXOaXYckO2nvzN2rp1qeTIEKrhLaC_nV5gZpOB2fnBC2Yr1KasERpdDO7I0xc4dqdLQXQRxrWgP6lKlkGKHziCrkvLEWqC7mXrRsS23m-qv4pELm0MikIqf-fl4wmwj7g42769APwAuoQdIgMnUOx2rT1ewkcW72py3wveX96oomdDyvIM6_y5uYALsTymc0xxr1yZOT9Gseypbjm-sa7byVaSbw2s9g\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"sec-ch-ua\\\":\\\"\\\\\\\"Chromium\\\\\\\";v=\\\\\\\"104\\\\\\\", \\\\\\\" Not A;Brand\\\\\\\";v=\\\\\\\"99\\\\\\\", \\\\\\\"Google Chrome\\\\\\\";v=\\\\\\\"104\\\\\\\"\\\",\\\"sec-ch-ua-mobile\\\":\\\"?0\\\",\\\"sec-ch-ua-platform\\\":\\\"\\\\\\\"Windows\\\\\\\"\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.9\\\",\\\"Content-Length\\\":\\\"2\\\",\\\"account\\\":\\\"1000000\\\",\\\"Content-Type\\\":\\\"application/json\\\"}\",\"responseHeaders\":\"{\\\"X-Frame-Options\\\":\\\"deny\\\",\\\"Cache-Control\\\":\\\"no-cache, no-store, must-revalidate, pre-check=0, post-check=0\\\",\\\"Server\\\":\\\"AKTO server\\\",\\\"X-Content-Type-Options\\\":\\\"nosniff\\\",\\\"Content-Encoding\\\":\\\"gzip\\\",\\\"Vary\\\":\\\"Accept-Encoding, User-Agent\\\",\\\"Content-Length\\\":\\\"150\\\",\\\"X-XSS-Protection\\\":\\\"1\\\",\\\"Content-Language\\\":\\\"en-US\\\",\\\"Date\\\":\\\"Tue, 30 Aug 2022 17:22:40 GMT\\\",\\\"Content-Type\\\":\\\"application/json;charset=utf-8\\\"}\",\"time\":\"1661880160\",\"contentType\":\"application/json;charset=utf-8\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"); + Key sampleKey = new Key(0, "http://localhost:8080/api/fetchTagConfigs", URLMethods.Method.POST,0,0,0); + SampleData sampleData = new SampleData(sampleKey,sampleList); + SampleDataDao.instance.getMCollection().drop(); + SampleDataDao.instance.insertOne(sampleData); + + SampleDataToSTI sampleDataToSTI = new SampleDataToSTI(); + List sampleDataDB = SampleDataDao.instance.findAll(new BasicDBObject()); + sampleDataToSTI.setSampleDataToSTI(sampleDataDB); + + assertEquals(sampleDataToSTI.getSingleTypeList().size(), 38); + assertEquals(sampleDataToSTI.getSingleTypeInfoMap().size(), 1); + + } +} diff --git a/apps/api-runtime/src/test/java/com/akto/utils/TestRedactSampleData.java b/apps/api-runtime/src/test/java/com/akto/utils/TestRedactSampleData.java new file mode 100644 index 0000000000..3aad9eea60 --- /dev/null +++ b/apps/api-runtime/src/test/java/com/akto/utils/TestRedactSampleData.java @@ -0,0 +1,287 @@ +package com.akto.utils; + +import com.akto.dto.HttpRequestParams; +import com.akto.dto.HttpResponseParams; +import com.akto.parsers.HttpCallParser; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.mongodb.BasicDBObject; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import java.io.IOException; +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class TestRedactSampleData { + + static ObjectMapper mapper = new ObjectMapper(); + static JsonFactory factory = mapper.getFactory(); + + private Set extractKeys(String payload) { + JsonParser jp = null; + JsonNode node = null; + try { + jp = factory.createParser(payload); + node = mapper.readTree(jp); + } catch (IOException e) { + throw new RuntimeException(e); + } + Map> requestParamMap = new HashMap<>(); + extractKeys(node,new ArrayList<>(), requestParamMap); + + return requestParamMap.keySet(); + } + + private void extractKeys(JsonNode node, List params, Map> values) { + if (node == null) return; + if (node.isValueNode()) { + String textValue = node.asText(); + String param = String.join("",params); + if (param.startsWith("#")) { + param = param.substring(1); + } + if (!values.containsKey(param)) { + values.put(param, new HashSet<>()); + } + values.get(param).add(textValue); + } else if (node.isArray()) { + ArrayNode arrayNode = (ArrayNode) node; + for(int i = 0; i < arrayNode.size(); i++) { + JsonNode arrayElement = arrayNode.get(i); + params.add("#$"); + extractKeys(arrayElement, params, values); + params.remove(params.size()-1); + } + } else { + Iterator fieldNames = node.fieldNames(); + while(fieldNames.hasNext()) { + String fieldName = fieldNames.next(); + params.add("#"+fieldName); + JsonNode fieldValue = node.get(fieldName); + extractKeys(fieldValue, params,values); + params.remove(params.size()-1); + } + } + + } + + private boolean testRedactDoneCorrect(HttpResponseParams original, HttpResponseParams redacted) throws IOException { + HttpRequestParams originalHttpRequestParams = original.requestParams; + HttpRequestParams redactedHttpRequestParams = redacted.requestParams; + + boolean result1 = originalHttpRequestParams.url.equals(redactedHttpRequestParams.url) && + originalHttpRequestParams.method.equals(redactedHttpRequestParams.method) && + originalHttpRequestParams.type.equals(redactedHttpRequestParams.type); + + if (!result1) return false; + + boolean result2 = original.type.equals(redacted.type) && + original.statusCode == redacted.statusCode && + Objects.equals(original.status, redacted.status) && + original.getTime() == redacted.getTime(); + + if (!result2) return false; + + Set originalHeaderNamesFromRequest = originalHttpRequestParams.getHeaders().keySet(); + Set redactedHeaderNamesFromRequest = redactedHttpRequestParams.getHeaders().keySet(); + + boolean result3 = originalHeaderNamesFromRequest.equals(redactedHeaderNamesFromRequest); + if (!result3) return false; + + Set originalHeaderNamesFromResponse = original.getHeaders().keySet(); + Set redactedHeaderNamesFromResponse = redacted.getHeaders().keySet(); + + boolean result4 = originalHeaderNamesFromResponse.equals(redactedHeaderNamesFromResponse); + if (!result4) return false; + + // test if key names in payload have not been changed + Set redactedReqKeys = extractKeys(redacted.requestParams.getPayload()); + Set originalReqKeys = extractKeys(original.requestParams.getPayload()); + if (!redactedReqKeys.equals(originalReqKeys)) { + return false; + } + + Set redactedRespKeys = extractKeys(redacted.getPayload()); + Set originalRespKeys = extractKeys(original.getPayload()); + if (!redactedRespKeys.equals(originalRespKeys)) { + + return false; + } + + + // test if all values have been hidden + + // 1. For headers + for (List val: redacted.getHeaders().values()) { + if (val.size() > 1) return false; + if (val.size() == 1 && !Objects.equals(val.get(0), RedactSampleData.redactValue)) return false; + } + + for (List val: redacted.requestParams.getHeaders().values()) { + if (val.size() > 1) return false; + if (val.size() == 1 && !Objects.equals(val.get(0), RedactSampleData.redactValue)) return false; + } + + // 2. For Payload + String responsePayload = redacted.getPayload(); + JsonParser jp = factory.createParser(responsePayload); + JsonNode node = mapper.readTree(jp); + boolean result5 = checkRedactPayload(node); + if (!result5) return false; + + String requestPayload = redacted.getRequestParams().getPayload(); + jp = factory.createParser(requestPayload); + node = mapper.readTree(jp); + boolean result6 = checkRedactPayload(node); + if (!result6) return false; + + // 3. For IP + if (!Objects.equals(redacted.getSourceIP(), RedactSampleData.redactValue)) return false; + + return true; + } + + public boolean checkRedactPayload(JsonNode parent) { + if (parent == null) return true; + + if (parent.isArray()) { + ArrayNode arrayNode = (ArrayNode) parent; + for(int i = 0; i < arrayNode.size(); i++) { + JsonNode arrayElement = arrayNode.get(i); + boolean result = false; + if (arrayElement.isValueNode()) { + result = arrayElement.textValue().equals(RedactSampleData.redactValue); + if (!result) return false; + } else { + result = checkRedactPayload(arrayElement); + if (!result) return false; + } + } + } else { + Iterator fieldNames = parent.fieldNames(); + while(fieldNames.hasNext()) { + boolean result = false; + String f = fieldNames.next(); + JsonNode fieldValue = parent.get(f); + if (fieldValue.isValueNode()) { + result = fieldValue.textValue().equals(RedactSampleData.redactValue); + if (!result) return false; + } else { + result = checkRedactPayload(fieldValue); + if (!result) return false; + } + } + } + + return true; + } + + @Test + public void redactNonJsonPayload() throws Exception { + Map> reqHeaders = new HashMap<>(); + reqHeaders.put("header1", Arrays.asList("valueA1","valueB1","valueC1")); + reqHeaders.put("header2", Arrays.asList("valueA1","valueB1","valueC1")); + + Map> respHeaders = new HashMap<>(); + respHeaders.put("header3", Arrays.asList("valueA1","valueB1","valueC1")); + + String reqPayload = "something random"; + + HttpRequestParams httpRequestParams = new HttpRequestParams( + "GET", "/api/books", "type",reqHeaders, reqPayload, 0 + ); + + String respPayload = "random response payload"; + + HttpResponseParams httpResponseParams = new HttpResponseParams( + "type", 200, "OK", respHeaders, respPayload, httpRequestParams, 0, "1000000", false, HttpResponseParams.Source.MIRRORING, "orig","172.0.0.1" + ); + + String redactedValue = RedactSampleData.redact(httpResponseParams); + + HttpResponseParams redactedHttpResponseParams = HttpCallParser.parseKafkaMessage(redactedValue); + + assertEquals(2, redactedHttpResponseParams.requestParams.getHeaders().size()); + assertEquals(1, redactedHttpResponseParams.getHeaders().size()); + assertEquals("{}", redactedHttpResponseParams.requestParams.getPayload()); + assertEquals("{}", redactedHttpResponseParams.getPayload()); + assertEquals(200, redactedHttpResponseParams.statusCode); + + } + + @Test + public void happy() throws Exception { +// String sample = "{\"method\":\"POST\",\"requestPayload\":\"{\\\"petId\\\":1,\\\"quantity\\\":0,\\\"id\\\":0,\\\"shipDate\\\":\\\"2022-01-04T20:10:16.578Z\\\",\\\"complete\\\":true,\\\"status\\\":\\\"avnebbesh@akto.io\\\"}\",\"responsePayload\":\"{\\\"id\\\":9223372036854772476,\\\"petId\\\":0,\\\"quantity\\\":0,\\\"shipDate\\\":\\\"2022-01-04T20:10:16.578+0000\\\",\\\"status\\\":\\\"av@gmail.com\\\",\\\"complete\\\":true, \\\"avav\\\": \\\"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g\\\"}\",\"ip\":\"null\",\"source\":\"HAR\",\"type\":\"HTTP/2\",\"akto_vxlan_id\":\"0\",\"path\":\"https://petstore.swagger.io/v2/store/order/1\",\"requestHeaders\":\"{\\\"Origin\\\":\\\"avneesh@gmail.com\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"TE\\\":\\\"trailers\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Content-Length\\\":\\\"128\\\",\\\"Content-Type\\\":\\\"application/json\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"+917021916328\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\",\"time\":\"1641327021\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + + Map> reqHeaders = new HashMap<>(); + reqHeaders.put("header1", Arrays.asList("valueA1","valueB1","valueC1")); + reqHeaders.put("header2", Collections.singletonList("valueA2")); + reqHeaders.put("header3", Collections.emptyList()); + + Map> respHeaders = new HashMap<>(); + respHeaders.put("header4", Arrays.asList("valueA1","valueB1","valueC1")); + respHeaders.put("header5", Collections.singletonList("valueA2")); + respHeaders.put("header6", Collections.emptyList()); + + Map reqPayloadMap = new HashMap<>(); + reqPayloadMap.put("req-name", "Avneesh"); + reqPayloadMap.put("req-email", "avneesh@akto.io"); + reqPayloadMap.put("req-id", 12345); + reqPayloadMap.put("req-teams", Arrays.asList("A", "B", "C")); + reqPayloadMap.put("req-privateJet", null); + reqPayloadMap.put("req-bikes", Collections.emptyList()); + + BasicDBObject friend1 = new BasicDBObject(); + friend1.append("name", "Ankush").append("email", "ankush@akto.io").append("teams", Arrays.asList("X","Y")); + + BasicDBObject friend2 = new BasicDBObject(); + friend2.append("name", "Ankita").append("email", "ankita@akto.io").append("teams", Collections.singletonList("X")); + + reqPayloadMap.put("friends", Arrays.asList(friend1, friend2)); + + String reqPayload = mapper.writeValueAsString(reqPayloadMap); + + HttpRequestParams httpRequestParams = new HttpRequestParams( + "GET", "/api/books", "type",reqHeaders, reqPayload, 0 + ); + + Map respPayloadMap = new HashMap<>(); + respPayloadMap.put("name", "avneesh"); + respPayloadMap.put("email", "avneesh@akto.io"); + respPayloadMap.put("success", true); + respPayloadMap.put("id", 12345); + respPayloadMap.put("teams", Arrays.asList("A", "B", "C")); + respPayloadMap.put("privateJet", null); + respPayloadMap.put("bikes", Collections.emptyList()); + respPayloadMap.put("friends", Arrays.asList(friend1, friend2)); + + String respPayload = mapper.writeValueAsString(respPayloadMap); + + HttpResponseParams httpResponseParams = new HttpResponseParams( + "type", 200, "OK", respHeaders, respPayload, httpRequestParams, 0, "1000000", false, HttpResponseParams.Source.MIRRORING, "orig","172.0.0.1" + ); + + String originalString = RedactSampleData.convertHttpRespToOriginalString(httpResponseParams); + HttpResponseParams originalHttpResponseParams = HttpCallParser.parseKafkaMessage(originalString); + + + String redactedValue = RedactSampleData.redact(httpResponseParams); + + HttpResponseParams redactedHttpResponseParams = HttpCallParser.parseKafkaMessage(redactedValue); + + try { + boolean result = testRedactDoneCorrect(originalHttpResponseParams, redactedHttpResponseParams); + Assertions.assertTrue(result); + } catch (Exception e) { + ; + Assertions.fail(); + } + + } +} diff --git a/apps/cft.template b/apps/cft.template new file mode 100644 index 0000000000..296caa6a76 --- /dev/null +++ b/apps/cft.template @@ -0,0 +1,1177 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "This template does a simple setup for all Akto modules. It sets up all modules on a single instance. If you want a scalable and flexible setup, please contact support@akto.io.", + "Parameters": { + "SubnetId": { + "Description": "Select existing subnet for Akto EC2 instance. If you are choosing private subnet, then make sure private subnet is configured to access public internet", + "Type": "AWS::EC2::Subnet::Id", + "ConstraintDescription": "must be an existing subnet" + }, + "KeyPair": { + "Description": "Select the key pair to connect to Akto EC2 instance", + "Type": "AWS::EC2::KeyPair::KeyName" + }, + "SourceLBs": { + "Description": "Comma separated list of loadblancer names ( eg: lb1, lb2, lb3 )", + "Type": "CommaDelimitedList", + "Default": "" + }, + "VpcId": { + "Description": "Select vpc", + "Type": "AWS::EC2::VPC::Id" + }, + "VpcCidrBlock": { + "Type": "String", + "Description": "Enter cidr block for selected vpc" + }, + "MongoIp": { + "Type": "String", + "Description": "Enter mongo's ip" + } + }, + "Mappings": { + "RegionMap": { + "af-south-1": { + "AMI": "ami-0adee70ff4394e3d5" + }, + "eu-north-1": { + "AMI": "ami-04e8b0e36ed3403dc" + }, + "ap-south-1": { + "AMI": "ami-09de362f44ba0a166" + }, + "eu-west-3": { + "AMI": "ami-0614433a16ab15878" + }, + "eu-west-2": { + "AMI": "ami-030770b178fa9d374" + }, + "eu-south-1": { + "AMI": "ami-0432f14b68c3e0273" + }, + "eu-west-1": { + "AMI": "ami-0bba0a4cb75835f71" + }, + "ap-northeast-3": { + "AMI": "ami-0253beba286f3e848" + }, + "ap-northeast-2": { + "AMI": "ami-0e1d09d8b7c751816" + }, + "me-south-1": { + "AMI": "ami-07a68e42e669daed0" + }, + "ap-northeast-1": { + "AMI": "ami-06ce6680729711877" + }, + "sa-east-1": { + "AMI": "ami-0656df2cc0dfd150a" + }, + "ca-central-1": { + "AMI": "ami-04c12937e87474def" + }, + "ap-east-1": { + "AMI": "ami-0b751f901b93720a5" + }, + "ap-southeast-1": { + "AMI": "ami-0adf622550366ea53" + }, + "ap-southeast-2": { + "AMI": "ami-03b836d87d294e89e" + }, + "eu-central-1": { + "AMI": "ami-094c442a8e9a67935" + }, + "ap-southeast-3": { + "AMI": "ami-0483d92a8124da6c9" + }, + "us-east-1": { + "AMI": "ami-065efef2c739d613b" + }, + "us-east-2": { + "AMI": "ami-07251f912d2a831a3" + }, + "us-west-1": { + "AMI": "ami-09b2f6d85764ec71b" + }, + "us-west-2": { + "AMI": "ami-0d08ef957f0e4722b" + } + } + }, + "Resources": { + "TrafficMirrorTarget": { + "Type": "AWS::EC2::TrafficMirrorTarget", + "Properties": { + "Description": "Traffic Mirror target set to network interface of Akto EC2 instance", + "Tags": [ + { + "Key": "Name", + "Value": { + "Fn::Join": [ + "", + [ + { + "Ref": "AWS::StackName" + }, + "-", + "Target" + ] + ] + } + }, + { + "Key": "Deployment", + "Value": "Akto-CloudFormation" + } + ], + "NetworkLoadBalancerArn": { + "Ref": "AktoNLB" + } + } + }, + "LBTrafficMirrorFilter": { + "Type": "AWS::EC2::TrafficMirrorFilter", + "Properties": { + "Description": "Traffic mirror filter for LBs and Target Groups", + "NetworkServices": [ + "amazon-dns" + ], + "Tags": [ + { + "Key": "Name", + "Value": { + "Fn::Join": [ + "", + [ + { + "Ref": "AWS::StackName" + }, + "-", + "Filter" + ] + ] + } + }, + { + "Key": "Deployment", + "Value": "Akto-CloudFormation" + } + ] + } + }, + "LambdaLogGroup": { + "Type": "AWS::Logs::LogGroup", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${CreateMirrorSession}" + }, + "RetentionInDays": 7 + } + }, + "LambdaBasicExecutionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] + }, + "Path": "/", + "Policies": [ + { + "PolicyName": "LBCreateTrafficMirrorSession", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeTrafficMirrorSessions", + "ec2:DescribeInstances", + "ec2:DescribeVpcs", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:DescribeTargetGroups", + "elasticloadbalancing:DescribeTargetHealth", + "ec2:DescribeTrafficMirrorFilters", + "ec2:DeleteTrafficMirrorFilterRule", + "ec2:CreateTrafficMirrorFilterRule", + "lambda:InvokeFunction" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "ec2:CreateTrafficMirrorSession" + ], + "Resource": [ + "arn:aws:ec2:*:*:traffic-mirror-session/*", + "arn:aws:ec2:*:*:network-interface/*", + { + "Fn::Join": [ + "", + [ + "arn:aws:ec2:*:*:traffic-mirror-target/", + { + "Ref": "TrafficMirrorTarget" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:aws:ec2:*:*:traffic-mirror-filter/*" + ] + ] + } + ] + }, + { + "Effect": "Allow", + "Action": [ + "ec2:DeleteTrafficMirrorSession" + ], + "Resource": [ + "arn:aws:ec2:*:*:traffic-mirror-session/*" + ] + } + ] + } + } + ] + } + }, + "GetAktoSetupDetailsLambdaBasicExecutionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] + }, + "Path": "/", + "Policies": [ + { + "PolicyName": "GetAktoSetupDetailsExecuteLambda", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeTrafficMirrorSessions", + "ec2:DescribeInstances", + "ec2:DescribeVpcs", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:DescribeTargetGroups", + "elasticloadbalancing:DescribeTargetHealth" + ], + "Resource": "*" + } + ] + } + } + ] + } + }, + "LambdaLogPermissions": { + "Type": "AWS::IAM::Policy", + "Properties": { + "Roles": [ + { + "Ref": "LambdaBasicExecutionRole" + } + ], + "PolicyName": { + "Fn::Sub": "${AWS::Region}-LambdaLogGroup" + }, + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource": [ + { + "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${CreateMirrorSession}" + }, + { + "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${CreateMirrorSession}:*" + }, + { + "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${CreateMirrorSession}:*:*" + } + ] + } + ] + } + } + }, + "CustomSourceENIs": { + "Type": "AWS::CloudFormation::CustomResource", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CreateMirrorSession", + "Arn" + ] + }, + "SourceLBs": { + "Ref": "SourceLBs" + }, + "MirrorTarget": { + "Ref": "TrafficMirrorTarget" + }, + "MirrorFilter": { + "Ref": "LBTrafficMirrorFilter" + } + }, + "DependsOn": "LambdaLogGroup" + }, + "GetAktoSetupDetails": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs12.x", + "Timeout": 60, + "Role": { + "Fn::GetAtt": [ + "GetAktoSetupDetailsLambdaBasicExecutionRole", + "Arn" + ] + }, + "Handler": "index.handler", + "Environment": { + "Variables": { + "TARGET_LB": { + "Ref": "AktoNLB" + } + } + }, + "Code": { + "ZipFile": "var aws = require('aws-sdk')\nvar response = require('cfn-response')\nvar targetLB = process.env.TARGET_LB;\nasync function getAktoLBDetails() {\n var elb = new aws.ELBv2();\n var params = {\n \"LoadBalancerArns\" : [targetLB]\n };\n\n\n console.log(\"GASD Getting Akto LB Details : \", JSON.stringify(params));\n\n var LBDesc = await elb.describeLoadBalancers(params).promise().catch(err=>{\n console.log(\"Error describing Akto LB\",err);\n });\n \n if(!LBDesc.LoadBalancers[0])\n {\n console.log(\"GASD Could not find Akto Load Balancer\");\n return ;\n }\n \n LBDesc = LBDesc.LoadBalancers[0];\n\n var LBName = targetLB.substring(targetLB.indexOf('/')+1);\n // LBname to get private ip of ENI of LB \n\n var eniDesc = 'ELB '+LBName;\n var ec2 = new aws.EC2();\n params = {\n Filters: [{\n 'Name' : 'description',\n 'Values' : [eniDesc]\n }]\n };\n \n var ENIDesc = await ec2.describeNetworkInterfaces(params).promise().catch(err=>{\n console.log(\"GASD Error getting ENI Details of ENI with Description \",eniDesc, \" error : \",err); \n });\n \n if(!ENIDesc.NetworkInterfaces[0])\n {\n console.log(\"GASD Coud not find Network Interface Details\");\n return ;\n \n }\n \n console.log(\"GASD ENIDesc: \",ENIDesc);\n \n LBDesc.PrivateIpAddress = ENIDesc.NetworkInterfaces[0].PrivateIpAddress;\n LBDesc.Subnets = LBDesc.AvailabilityZones.map(x=>x.SubnetId);\n \n \n \n console.log(\"GASD LoadBalancerDescription : \",JSON.stringify(LBDesc));\n return LBDesc;\n \n} \nexports.handler = async function(event, context) {\n if (event.RequestType == \"Delete\") {\n await response.send(event, context, \"SUCCESS\");\n return;\n } \n var responseData = {}\n responseData['successEnis'] = JSON.stringify([]); \n var aktoDetails = await getAktoLBDetails();\n responseData['kafkaIp'] = aktoDetails.PrivateIpAddress;\n responseData['SubnetId'] = aktoDetails.Subnets;\n responseData['VpcId'] = aktoDetails.VpcId;\n console.log(\"GASD responseData\", responseData);\n await response.send(event, context, \"SUCCESS\", responseData);\n};\n" + } + } + }, + "CustomSourceAktoSetupDetails": { + "Type": "AWS::CloudFormation::CustomResource", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "GetAktoSetupDetails", + "Arn" + ] + } + } + }, + "CreateMirrorSession": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs12.x", + "Timeout": 60, + "Role": { + "Fn::GetAtt": [ + "LambdaBasicExecutionRole", + "Arn" + ] + }, + "Handler": "index.handler", + "Environment": { + "Variables": { + "ELB_NAMES": { + "Fn::Join": [ + ",", + { + "Ref": "SourceLBs" + } + ] + }, + "TRAFFIC_MIRROR_FILTER_ID": { + "Ref": "LBTrafficMirrorFilter" + }, + "TRAFFIC_MIRROR_TARGET_ID": { + "Ref": "TrafficMirrorTarget" + }, + "TARGET_LB": { + "Ref": "AktoNLB" + }, + "SAVE_COLLECTION_NAMES_LAMBDA_ARN": { + "Fn::GetAtt": [ + "SaveCollectionNames", + "Arn" + ] + } + } + }, + "Code": { + "S3Bucket": { + "Fn::Sub": "akto-setup-${AWS::Region}" + }, + "S3Key": "templates/create-mirror-session.zip" + }, + "Description": "Auto create mirroring configuration", + "TracingConfig": { + "Mode": "Active" + } + } + }, + "PeriodicEventRule": { + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Generate an event periodically", + "Name": { + "Fn::Join": [ + "", + [ + { + "Ref": "AWS::StackName" + }, + "-", + "PeriodicRule" + ] + ] + }, + "ScheduleExpression": "rate(15 minutes)", + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "CreateMirrorSession", + "Arn" + ] + }, + "Id": { + "Ref": "CreateMirrorSession" + } + } + ] + } + }, + "PeriodicEventPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "FunctionName": { + "Fn::GetAtt": [ + "CreateMirrorSession", + "Arn" + ] + }, + "Action": "lambda:InvokeFunction", + "Principal": "events.amazonaws.com", + "SourceAccount": { + "Ref": "AWS::AccountId" + }, + "SourceArn": { + "Fn::GetAtt": [ + "PeriodicEventRule", + "Arn" + ] + } + } + }, + "LambdaVPCAccessRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] + }, + "Path": "/", + "Policies": [ + { + "PolicyName": "LambdaBasicAccessVPCPolicy", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeInstances", + "ec2:CreateNetworkInterface", + "ec2:DeleteNetworkInterface", + "ec2:AttachNetworkInterface" + ], + "Resource": "*" + } + ] + } + } + ] + } + }, + "LambdaSecurityGroupVPC": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "VpcId": { + "Fn::GetAtt": [ + "CustomSourceAktoSetupDetails", + "VpcId" + ] + }, + "GroupDescription": "Security group is required to create a lambda inside a VPC", + "SecurityGroupEgress": [ + { + "IpProtocol": "tcp", + "FromPort": 9092, + "ToPort": 9092, + "CidrIp": "0.0.0.0/0" + } + ] + } + }, + "SaveCollectionNames": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs12.x", + "Timeout": 60, + "Role": { + "Fn::GetAtt": [ + "LambdaVPCAccessRole", + "Arn" + ] + }, + "Handler": "nodejs/index.handler", + "VpcConfig": { + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "LambdaSecurityGroupVPC", + "GroupId" + ] + } + ], + "SubnetIds": { + "Fn::GetAtt": [ + "CustomSourceAktoSetupDetails", + "SubnetId" + ] + } + }, + "Environment": { + "Variables": { + "PRIVATE_IP": { + "Fn::GetAtt": [ + "CustomSourceAktoSetupDetails", + "kafkaIp" + ] + }, + "SUCCESS_ENIS": { + "Fn::GetAtt": [ + "CustomSourceAktoSetupDetails", + "successEnis" + ] + } + } + }, + "Code": { + "S3Bucket": { + "Fn::Sub": "akto-setup-${AWS::Region}" + }, + "S3Key": "templates/mirroring-collections-split.zip" + }, + "Description": "Send collection name to id mapping to Akto modules", + "TracingConfig": { + "Mode": "Active" + } + } + }, + "IamInstanceProfile": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Path": "/", + "Roles": [ + { + "Ref": "RefreshHandlerLambdaBasicExecutionRole" + } + ] + } + }, + "AktoContextAnalyzerSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "VpcId": { + "Ref": "VpcId" + }, + "GroupDescription": "Enable the ports Akto requires (22, 9092)", + "SecurityGroupIngress": [ + { + "IpProtocol": "tcp", + "FromPort": 22, + "ToPort": 22, + "CidrIp": { + "Ref": "VpcCidrBlock" + } + }, + { + "IpProtocol": "tcp", + "FromPort": 9092, + "ToPort": 9092, + "CidrIp": { + "Ref": "VpcCidrBlock" + } + } + ], + "SecurityGroupEgress": [] + } + }, + "AktoContextAnalyzerASGLaunchConfiguration": { + "Type": "AWS::AutoScaling::LaunchConfiguration", + "Properties": { + "ImageId": { + "Fn::FindInMap": [ + "RegionMap", + { + "Ref": "AWS::Region" + }, + "AMI" + ] + }, + "InstanceType": "m5.xlarge", + "KeyName": { + "Ref": "KeyPair" + }, + "LaunchConfigurationName": "AktoContextAnalyzerASGLaunchConfiguration", + "AssociatePublicIpAddress": "false", + "SecurityGroups": [ + { + "Ref": "AktoContextAnalyzerSecurityGroup" + } + ], + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/xvda", + "Ebs": { + "VolumeType": "gp2", + "DeleteOnTermination": "true", + "VolumeSize": "50", + "Encrypted": true + } + } + ], + "MetadataOptions": { + "HttpTokens": "required" + }, + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "\n", + [ + "#!/bin/bash -xe", + { + "Fn::Sub": "export AKTO_MONGO_CONN='${MongoIp}'" + }, + "touch /tmp/hello.txt", + "touch ~/hello.txt", + "sudo yum update -y", + "sudo yum install -y python python-setuptools", + "sudo yum install -y docker", + "sudo dockerd&", + "sudo mkdir -p /opt/aws/bin", + "export COMPOSE_FILE=docker-compose-context-analyser.yml", + "sudo wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz", + "sudo python -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-latest.tar.gz", + "curl -fsSL 'https://raw.githubusercontent.com/akto-api-security/infra/feature/segregation_2/cf-deploy-akto' > cf-deploy-akto", + "sudo chmod 700 cf-deploy-akto", + "./cf-deploy-akto < <(echo 'test')", + "sudo echo >> ~/akto/infra/docker-context-analyser.env", + "sudo echo AKTO_MONGO_CONN=mongodb://$AKTO_MONGO_CONN:27017/admini >> ~/akto/infra/docker-context-analyser.env", + "export TOKEN=$(curl -X PUT 'http://169.254.169.254/latest/api/token' -H 'X-aws-ec2-metadata-token-ttl-seconds: 600')", + { + "Fn::Join": [ + ":", + [ + "export AKTO_CURRENT_INSTANCE_IP=$(curl -H \"X-aws-ec2-metadata-token", + "$TOKEN\" -v http://169.254.169.254/latest/meta-data/local-ipv4)" + ] + ] + }, + "echo AKTO_CURRENT_INSTANCE_IP=$AKTO_CURRENT_INSTANCE_IP >> ~/akto/infra/docker-context-analyser.env", + "curl -fsSL 'https://raw.githubusercontent.com/akto-api-security/infra/feature/segregation_2/cf-deploy-akto-start' > cf-deploy-akto-start", + "sudo chmod 700 cf-deploy-akto-start", + "./cf-deploy-akto-start < <(echo 'test')" + ] + ] + } + } + } + }, + "AktoContextAnalyzerAutoScalingGroup": { + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Properties": { + "AutoScalingGroupName": "AktoContextAnalyzerAutoScalingGroup", + "VPCZoneIdentifier": [ + { + "Ref": "SubnetId" + } + ], + "LaunchConfigurationName": { + "Ref": "AktoContextAnalyzerASGLaunchConfiguration" + }, + "MaxSize": "1", + "MinSize": "1" + } + }, + "AktoContextAnalyzerInstanceRefreshHandler": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "FunctionName": "AktoContextAnalyzerInstanceRefreshHandler", + "Runtime": "nodejs12.x", + "Timeout": 30, + "Role": { + "Fn::GetAtt": [ + "InstanceRefreshHandlerLambdaRole", + "Arn" + ] + }, + "Code": { + "ZipFile": "var aws = require('aws-sdk'); var autoscaling = new aws.AutoScaling(); exports.handler = function(event, context) {\n var params = {\n AutoScalingGroupName: 'AktoContextAnalyzerAutoScalingGroup', \n Preferences: {\n InstanceWarmup: 200, \n MinHealthyPercentage: 0\n }\n };\n \n autoscaling.startInstanceRefresh(params, function(err, data) {\n if(err) { console.log(err) }\n else { console.log(data) }\n })\n};\n" + } + } + }, + "RefreshHandlerLambdaBasicExecutionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] + }, + "Policies": [ + { + "PolicyName": "InvokeLambdaPolicy", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "DashboardInstanceRefreshHandler", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "TrafficMirroringInstanceRefreshHandler", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "AktoContextAnalyzerInstanceRefreshHandler", + "Arn" + ] + } + ], + "Action": "lambda:InvokeFunction" + } + ] + } + } + ] + } + }, + "AktoSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "VpcId": { + "Ref": "VpcId" + }, + "GroupDescription": "Enable the ports Akto requires (22, 4789, 8000, 9092)", + "SecurityGroupIngress": [ + { + "IpProtocol": "tcp", + "FromPort": 22, + "ToPort": 22, + "CidrIp": { + "Ref": "VpcCidrBlock" + } + }, + { + "IpProtocol": "tcp", + "FromPort": 9092, + "ToPort": 9092, + "CidrIp": { + "Ref": "VpcCidrBlock" + } + }, + { + "IpProtocol": "udp", + "FromPort": 4789, + "ToPort": 4789, + "CidrIp": { + "Ref": "VpcCidrBlock" + } + }, + { + "IpProtocol": "tcp", + "FromPort": 8000, + "ToPort": 8000, + "CidrIp": { + "Ref": "VpcCidrBlock" + } + } + ], + "SecurityGroupEgress": [] + } + }, + "AktoASGLaunchConfiguration": { + "Type": "AWS::AutoScaling::LaunchConfiguration", + "DependsOn": [ + "AktoNLB" + ], + "Properties": { + "ImageId": { + "Fn::FindInMap": [ + "RegionMap", + { + "Ref": "AWS::Region" + }, + "AMI" + ] + }, + "InstanceType": "m5.xlarge", + "KeyName": { + "Ref": "KeyPair" + }, + "LaunchConfigurationName": "AktoASGLaunchConfiguration", + "AssociatePublicIpAddress": "false", + "IamInstanceProfile": { + "Ref": "IamInstanceProfile" + }, + "SecurityGroups": [ + { + "Ref": "AktoSecurityGroup" + } + ], + "BlockDeviceMappings": [ + { + "DeviceName": "/dev/xvda", + "Ebs": { + "VolumeType": "gp2", + "DeleteOnTermination": "true", + "VolumeSize": "50", + "Encrypted": true + } + } + ], + "MetadataOptions": { + "HttpTokens": "required" + }, + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "\n", + [ + "#!/bin/bash -xe", + { + "Fn::Sub": "export AKTO_MONGO_CONN='${MongoIp}'" + }, + { + "Fn::Sub": "export AKTO_KAFKA_IP='${AktoNLB.DNSName}'" + }, + "touch /tmp/hello.txt", + "touch ~/hello.txt", + "sudo yum update -y", + "sudo yum install -y python python-setuptools", + "sudo yum install -y docker", + "sudo dockerd&", + "sudo mkdir -p /opt/aws/bin", + "export COMPOSE_FILE=docker-compose-runtime.yml", + "sudo wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz", + "sudo python -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-latest.tar.gz", + "curl -fsSL 'https://raw.githubusercontent.com/akto-api-security/infra/feature/segregation_2/cf-deploy-akto' > cf-deploy-akto", + "sudo chmod 700 cf-deploy-akto", + "./cf-deploy-akto < <(echo 'test')", + "sudo echo >> ~/akto/infra/docker-runtime.env", + "sudo echo AKTO_MONGO_CONN=mongodb://$AKTO_MONGO_CONN:27017/admini >> ~/akto/infra/docker-runtime.env", + "sudo echo AKTO_KAFKA_IP=$AKTO_KAFKA_IP >> ~/akto/infra/.env", + "curl -fsSL 'https://raw.githubusercontent.com/akto-api-security/infra/feature/segregation_2/cf-deploy-akto-start' > cf-deploy-akto-start", + "sudo chmod 700 cf-deploy-akto-start", + "./cf-deploy-akto-start < <(echo 'test')" + ] + ] + } + } + } + }, + "AktoAutoScalingGroup": { + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Properties": { + "AutoScalingGroupName": "AktoAutoScalingGroup", + "VPCZoneIdentifier": [ + { + "Ref": "SubnetId" + } + ], + "LaunchConfigurationName": { + "Ref": "AktoASGLaunchConfiguration" + }, + "TargetGroupARNs": [ + { + "Ref": "AktoTrafficMirroringTargetGroup" + }, + { + "Ref": "AktoKafkaTargetGroup" + } + ], + "MaxSize": "10", + "MinSize": "1" + } + }, + "AktoTargetTrackingNetworkPolicy": { + "Type": "AWS::AutoScaling::ScalingPolicy", + "Properties": { + "PolicyType": "TargetTrackingScaling", + "AutoScalingGroupName": { + "Ref": "AktoAutoScalingGroup" + }, + "EstimatedInstanceWarmup": 30, + "TargetTrackingConfiguration": { + "PredefinedMetricSpecification": { + "PredefinedMetricType": "ASGAverageNetworkIn" + }, + "TargetValue": 200000000 + } + } + }, + "AktoNLB": { + "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", + "Properties": { + "Type": "network", + "Scheme": "internal", + "IpAddressType": "ipv4", + "Subnets": [ + { + "Ref": "SubnetId" + } + ], + "Name": "AktoNLB", + "LoadBalancerAttributes": [ + { + "Key": "load_balancing.cross_zone.enabled", + "Value": "true" + } + ] + } + }, + "AktoTrafficMirroringTargetGroup": { + "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", + "Properties": { + "Port": "4789", + "Protocol": "UDP", + "HealthCheckEnabled": "true", + "HealthCheckIntervalSeconds": 10, + "HealthCheckPath": "/metrics", + "HealthCheckPort": "8000", + "HealthCheckProtocol": "HTTP", + "HealthCheckTimeoutSeconds": 6, + "HealthyThresholdCount": 2, + "UnhealthyThresholdCount": 2, + "TargetType": "instance", + "VpcId": { + "Ref": "VpcId" + }, + "Targets": [], + "Name": "AktoTrafficMirroringTargetGroup" + } + }, + "AktoTrafficMirroringListener": { + "Type": "AWS::ElasticLoadBalancingV2::Listener", + "Properties": { + "LoadBalancerArn": { + "Ref": "AktoNLB" + }, + "Port": "4789", + "Protocol": "UDP", + "DefaultActions": [ + { + "Type": "forward", + "TargetGroupArn": { + "Ref": "AktoTrafficMirroringTargetGroup" + } + } + ] + } + }, + "AktoKafkaTargetGroup": { + "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", + "Properties": { + "Port": "9092", + "Protocol": "TCP", + "TargetType": "instance", + "HealthCheckEnabled": "true", + "HealthCheckIntervalSeconds": 10, + "HealthCheckPath": "/metrics", + "HealthCheckPort": "8000", + "HealthCheckProtocol": "HTTP", + "HealthCheckTimeoutSeconds": 6, + "HealthyThresholdCount": 2, + "UnhealthyThresholdCount": 2, + "VpcId": { + "Ref": "VpcId" + }, + "Targets": [], + "Name": "AktoKafkaTargetGroup" + } + }, + "AktoKafkaListener": { + "Type": "AWS::ElasticLoadBalancingV2::Listener", + "Properties": { + "LoadBalancerArn": { + "Ref": "AktoNLB" + }, + "Port": "9092", + "Protocol": "TCP", + "DefaultActions": [ + { + "Type": "forward", + "TargetGroupArn": { + "Ref": "AktoKafkaTargetGroup" + } + } + ] + } + }, + "DashboardInstanceRefreshHandler": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "FunctionName": "DashboardInstanceRefreshHandler", + "Runtime": "nodejs12.x", + "Timeout": 30, + "Role": { + "Fn::GetAtt": [ + "InstanceRefreshHandlerLambdaRole", + "Arn" + ] + }, + "Code": { + "ZipFile": "var aws = require('aws-sdk'); var autoscaling = new aws.AutoScaling(); exports.handler = function(event, context) {\n var params = {\n AutoScalingGroupName: 'AktoDashboardAutoScalingGroup', \n Preferences: {\n InstanceWarmup: 200, \n MinHealthyPercentage: 0\n }\n };\n \n autoscaling.startInstanceRefresh(params, function(err, data) {\n if(err) { console.log(err) }\n else { console.log(data) }\n })\n};\n" + } + } + }, + "TrafficMirroringInstanceRefreshHandler": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.handler", + "FunctionName": "TrafficMirroringInstanceRefreshHandler", + "Runtime": "nodejs12.x", + "Timeout": 30, + "Role": { + "Fn::GetAtt": [ + "InstanceRefreshHandlerLambdaRole", + "Arn" + ] + }, + "Code": { + "ZipFile": "var aws = require('aws-sdk'); var autoscaling = new aws.AutoScaling(); exports.handler = function(event, context) {\n var params = {\n AutoScalingGroupName: 'AktoAutoScalingGroup', \n Preferences: {\n InstanceWarmup: 200, \n MinHealthyPercentage: 0\n }\n };\n \n autoscaling.startInstanceRefresh(params, function(err, data) {\n if(err) { console.log(err) }\n else { console.log(data) }\n })\n};\n" + } + } + }, + "InstanceRefreshHandlerLambdaRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + }, + "Action": [ + "sts:AssumeRole" + ] + } + ] + }, + "Path": "/service-role/", + "Policies": [ + { + "PolicyName": "lambdaExecution-DashboardInstanceRefreshHandler", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogGroup" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "autoscaling:StartInstanceRefresh", + "autoscaling:Describe*", + "autoscaling:UpdateAutoScalingGroup", + "ec2:CreateLaunchTemplateVersion", + "ec2:DescribeLaunchTemplates", + "ec2:RunInstances" + ], + "Resource": "*" + } + ] + } + } + ] + } + } + }, + "Outputs": { + "AktoNLB": { + "Value": { + "Ref": "AktoNLB" + }, + "Description": "Arn of Akto Network Load Balancer" + } + } +} \ No newline at end of file diff --git a/apps/dashboard/.babelrc b/apps/dashboard/.babelrc new file mode 100644 index 0000000000..d5e9936eb6 --- /dev/null +++ b/apps/dashboard/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-react"], + "plugins": ["@babel/plugin-proposal-object-rest-spread","@babel/plugin-transform-runtime"] +} \ No newline at end of file diff --git a/apps/dashboard/.gitignore b/apps/dashboard/.gitignore new file mode 100644 index 0000000000..3b3dfd0085 --- /dev/null +++ b/apps/dashboard/.gitignore @@ -0,0 +1,9 @@ +node_modules +target +yarn-error.log +web/dist +.settings +.project +.classpath +.idea/ +.vscode/launch.json diff --git a/apps/dashboard/Dockerfile b/apps/dashboard/Dockerfile new file mode 100644 index 0000000000..6dd4882952 --- /dev/null +++ b/apps/dashboard/Dockerfile @@ -0,0 +1,8 @@ +FROM jetty:9.4-jre8 +USER root +RUN apt-get update -y +RUN apt-get install -y --no-install-recommends libpcap-dev +ADD ./target/dashboard.war /var/lib/jetty/webapps/root.war +RUN echo "--module=http-forwarded" > /var/lib/jetty/start.d/http-forwarded.ini +RUN echo "jetty.httpConfig.sendServerVersion=false" > /var/lib/jetty/start.d/server.ini +EXPOSE 8080 \ No newline at end of file diff --git a/apps/dashboard/README.md b/apps/dashboard/README.md new file mode 100644 index 0000000000..688eee90a0 --- /dev/null +++ b/apps/dashboard/README.md @@ -0,0 +1,19 @@ +To setup node modules +`npm install` + +To build Vue +`npm run build` + +Build dao by running this command from dao project +`~/Downloads/apache-maven-3.6.3/bin/mvn install` + +Run using +` ~/Downloads/apache-maven-3.6.3/bin/mvn jetty:run` + +Open +`http://localhost:8080/` + +How to setup git config locally +`https://gist.github.com/Jonalogy/54091c98946cfe4f8cdab2bea79430f9` + + diff --git a/apps/dashboard/package-lock.json b/apps/dashboard/package-lock.json new file mode 100644 index 0000000000..5f6769083e --- /dev/null +++ b/apps/dashboard/package-lock.json @@ -0,0 +1,29527 @@ +{ + "name": "hello01", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "hello01", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.13.10", + "@babel/eslint-parser": "^7.13.10", + "@babel/plugin-proposal-object-rest-spread": "^7.13.8", + "@babel/plugin-transform-react-jsx": "7.12.11", + "@babel/plugin-transform-runtime": "^7.13.10", + "@babel/preset-env": "^7.13.10", + "@babel/preset-react": "7.0.0", + "@emotion/react": "^11.9.3", + "@emotion/styled": "^11.9.3", + "@fortawesome/fontawesome-svg-core": "^1.2.36", + "@fortawesome/free-brands-svg-icons": "^5.15.3", + "@fortawesome/free-regular-svg-icons": "^5.15.3", + "@fortawesome/free-solid-svg-icons": "^5.15.4", + "@fortawesome/react-fontawesome": "^0.2.0", + "@fortawesome/vue-fontawesome": "^2.0.2", + "@material-ui/core": "^4.12.4", + "@mdi/font": "^5.6.55", + "@mui/material": "^5.9.2", + "@mui/x-date-pickers": "^5.0.0", + "@puppeteer/replay": "^2.7.1", + "are-you-es5": "^2.1.1", + "axios": "^0.21.1", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "dayjs": "^1.11.5", + "fiber": "^1.0.4", + "file-saver": "^2.0.5", + "highcharts": "^9.0.1", + "highcharts-vue": "^1.0.4", + "puppeteer": "^19.4.1", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-flow-renderer": "^10.3.12", + "tippy.js": "^6.3.1", + "tiptap": "^1.32.1", + "tiptap-extensions": "^1.35.1", + "vue": "^2.5.11", + "vue-router": "^3.5.1", + "vuera": "^0.2.7", + "vuetify": "^2.4.3", + "vuex": "^3.6.2", + "zustand": "^4.0.0" + }, + "devDependencies": { + "babel-core": "^6.0.0", + "babel-eslint": "^7.0.0", + "babel-loader": "^8.2.2", + "babel-plugin-transform-regenerator": "^6.26.0", + "babel-plugin-transform-runtime": "^6.0.0", + "babel-polyfill": "^6.26.0", + "babel-preset-es2015": "^6.0.0", + "babel-preset-stage-2": "^6.0.0", + "babel-register": "^6.0.0", + "cross-env": "^5.0.5", + "css-loader": "^0.28.7", + "file-loader": "^1.1.4", + "sass": "^1.25.0", + "sass-loader": "^7.2.0", + "sw-precache-webpack-plugin": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.3.0", + "vue-loader": "^13.0.5", + "vue-template-compiler": "^2.4.4", + "webpack": "^3.6.0", + "webpack-dev-server": "^2.9.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", + "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.9.tgz", + "integrity": "sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.9", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.9", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.18.9.tgz", + "integrity": "sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ==", + "dependencies": { + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.11.0", + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.9.tgz", + "integrity": "sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==", + "dependencies": { + "@babel/types": "^7.18.9", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-react-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.18.6.tgz", + "integrity": "sha512-2ndBVP5f9zwHWQeBr5EgqTAvFhPDViMW969bbJzRhKUUylnC39CdFZdVmqk+UtkxIpwm/efPgm3SzXUSlJnjAw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-react-jsx-experimental": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.11.tgz", + "integrity": "sha512-4oGVOekPI8dh9JphkPXC68iIuP6qp/RPbaPmorRmEFbRAHZjSqxPjqHudn18GVDPgCuFM/KdFXc63C17Ygfa9w==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.10", + "@babel/helper-module-imports": "^7.12.5", + "@babel/types": "^7.12.11" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", + "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", + "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", + "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", + "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", + "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.9.tgz", + "integrity": "sha512-cG2ru3TRAL6a60tfQflpEfs4ldiPwF6YW3zfJiRgmoFVIaC1vGnBBgatfec+ZUziPHkHSaXAuEck3Cdkf3eRpQ==", + "dependencies": { + "@babel/helper-function-name": "^7.18.9", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.9.tgz", + "integrity": "sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz", + "integrity": "sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", + "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.18.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", + "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", + "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", + "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", + "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", + "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", + "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-identifier": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", + "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.11.tgz", + "integrity": "sha512-5nWOw6mTylaFU72BdZfa0dP1HsGdY3IMExpxn8LBE8dNmkQjB+W+sR+JwIdtbzkPvVuFviT3zyNbSUkuVTVxbw==", + "dependencies": { + "@babel/helper-builder-react-jsx": "^7.10.4", + "@babel/helper-builder-react-jsx-experimental": "^7.12.11", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz", + "integrity": "sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz", + "integrity": "sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz", + "integrity": "sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.1", + "babel-plugin-polyfill-corejs3": "^0.5.2", + "babel-plugin-polyfill-regenerator": "^0.3.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", + "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz", + "integrity": "sha512-XNRwQUXYMP7VLuy54cr/KS/WeL3AZeORhrmeZ7iewgu+X2eBqmpaLI/hzqr9ZxCeUoq0ASK4GUzSM0BDhZkLFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.9.tgz", + "integrity": "sha512-75pt/q95cMIHWssYtyfjVlvI+QEZQThQbKvR9xH+F/Agtw/s4Wfc2V9Bwd/P39VtixB7oWxGdH4GteTTwYJWMg==", + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.6", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.6", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.1", + "babel-plugin-polyfill-corejs3": "^0.5.2", + "babel-plugin-polyfill-regenerator": "^0.3.1", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.0.0.tgz", + "integrity": "sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-transform-react-display-name": "^7.0.0", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/plugin-transform-react-jsx-self": "^7.0.0", + "@babel/plugin-transform-react-jsx-source": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", + "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.9.tgz", + "integrity": "sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.9", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.9", + "@babel/types": "^7.18.9", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz", + "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@date-io/core": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@date-io/core/-/core-2.15.0.tgz", + "integrity": "sha512-3CRvQUEK7aF87NUOwcTtmJ2Rc1kN0D4jFQUfRoanuAnE4o5HzHx4E2YenjaKjSPWeZYiWG6ZhDomx5hp1AaCJA==" + }, + "node_modules/@date-io/date-fns": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@date-io/date-fns/-/date-fns-2.15.0.tgz", + "integrity": "sha512-hkVeLm0jijHS2F9YVQcf0LSlD55w9xPvvIfuxDE0XWNXOTcRAAhqw2aqOxyeGbmHxc5U4HqyPZaqs9tfeTsomQ==", + "dependencies": { + "@date-io/core": "^2.15.0" + }, + "peerDependencies": { + "date-fns": "^2.0.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + } + } + }, + "node_modules/@date-io/dayjs": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@date-io/dayjs/-/dayjs-2.15.0.tgz", + "integrity": "sha512-wgYzwaXr9KxkHNYxrOb1t8fYLfAdjIf0Q86qdVCwANObcvyGcPBm0uFtpPK7ApeE4DJUlbuG0IX75TtO+uITwQ==", + "dependencies": { + "@date-io/core": "^2.15.0" + }, + "peerDependencies": { + "dayjs": "^1.8.17" + }, + "peerDependenciesMeta": { + "dayjs": { + "optional": true + } + } + }, + "node_modules/@date-io/luxon": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@date-io/luxon/-/luxon-2.15.0.tgz", + "integrity": "sha512-CxTRCo5AM96ainnYaTpe1NS9GiA78SIgXBScgeAresCS20AvMcOd5XKerDj+y/KLhbSQbU6WUDqG9QcsrImXyQ==", + "dependencies": { + "@date-io/core": "^2.15.0" + }, + "peerDependencies": { + "luxon": "^1.21.3 || ^2.x" + }, + "peerDependenciesMeta": { + "luxon": { + "optional": true + } + } + }, + "node_modules/@date-io/moment": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@date-io/moment/-/moment-2.15.0.tgz", + "integrity": "sha512-AcYBjl3EnEGsByaM5ir644CKbhgJsgc1iWFa9EXfdb4fQexxOC8oCdPAurK2ZDTwg62odyyKa/05YE7ElYh5ag==", + "dependencies": { + "@date-io/core": "^2.15.0" + }, + "peerDependencies": { + "moment": "^2.24.0" + }, + "peerDependenciesMeta": { + "moment": { + "optional": true + } + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.9.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.9.5.tgz", + "integrity": "sha512-n+9y6TSsvAsOc0hXmdVtqgU6B+ils+zrzTZGg1aV2BkZtBbL7DiasLSpu3fUFwXOkYm63j7+649yb+HhyZxYSA==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/plugin-syntax-jsx": "^7.17.12", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.5", + "@emotion/serialize": "^1.0.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.0.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.9.3.tgz", + "integrity": "sha512-0dgkI/JKlCXa+lEXviaMtGBL0ynpx4osh7rjOXE71q9bIF8G+XhJgvi+wDu0B0IdCVx37BffiwXlN9I3UuzFvg==", + "dependencies": { + "@emotion/memoize": "^0.7.4", + "@emotion/sheet": "^1.1.1", + "@emotion/utils": "^1.0.0", + "@emotion/weak-memoize": "^0.2.5", + "stylis": "4.0.13" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.3.tgz", + "integrity": "sha512-RFg04p6C+1uO19uG8N+vqanzKqiM9eeV1LDOG3bmkYmuOj7NbKNlFC/4EZq5gnwAIlcC/jOT24f8Td0iax2SXA==", + "dependencies": { + "@emotion/memoize": "^0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", + "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" + }, + "node_modules/@emotion/react": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.9.3.tgz", + "integrity": "sha512-g9Q1GcTOlzOEjqwuLF/Zd9LC+4FljjPjDfxSM7KmEakm+hsHXk+bYZ2q+/hTJzr0OUNkujo72pXLQvXj6H+GJQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@emotion/babel-plugin": "^11.7.1", + "@emotion/cache": "^11.9.3", + "@emotion/serialize": "^1.0.4", + "@emotion/utils": "^1.1.0", + "@emotion/weak-memoize": "^0.2.5", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.4.tgz", + "integrity": "sha512-1JHamSpH8PIfFwAMryO2bNka+y8+KA5yga5Ocf2d7ZEiJjb7xlLW7aknBGZqJLajuLOvJ+72vN+IBSwPlXD1Pg==", + "dependencies": { + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.4", + "@emotion/unitless": "^0.7.5", + "@emotion/utils": "^1.0.0", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.1.tgz", + "integrity": "sha512-J3YPccVRMiTZxYAY0IOq3kd+hUP8idY8Kz6B/Cyo+JuXq52Ek+zbPbSQUrVQp95aJ+lsAW7DPL1P2Z+U1jGkKA==" + }, + "node_modules/@emotion/styled": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.9.3.tgz", + "integrity": "sha512-o3sBNwbtoVz9v7WB1/Y/AmXl69YHmei2mrVnK7JgyBJ//Rst5yqPZCecEJlMlJrFeWHp+ki/54uN265V2pEcXA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@emotion/babel-plugin": "^11.7.1", + "@emotion/is-prop-valid": "^1.1.3", + "@emotion/serialize": "^1.0.4", + "@emotion/utils": "^1.1.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "node_modules/@emotion/utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz", + "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.4.tgz", + "integrity": "sha512-f1witbwycL9cTENJegcmcZRYyawAFbm8+c6IirLmwbbpqz46wyjbQYLuxOc7weXFXfB7QR8/Vd2u5R3q6JYD9g==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz", + "integrity": "sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, + "node_modules/@fortawesome/vue-fontawesome": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-2.0.8.tgz", + "integrity": "sha512-SRmP0q9Ox4zq8ydDR/hrH+23TVU1bdwYVnugLVaAIwklOHbf56gx6JUGlwES7zjuNYqzKgl8e39iYf6ph8qSQw==", + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "vue": "~2" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "peer": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "peer": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@material-ui/core": { + "version": "4.12.4", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.4.tgz", + "integrity": "sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ==", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.11.5", + "@material-ui/system": "^4.12.2", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/core/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/@material-ui/styles": { + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", + "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.5.1", + "jss-plugin-camel-case": "^10.5.1", + "jss-plugin-default-unit": "^10.5.1", + "jss-plugin-global": "^10.5.1", + "jss-plugin-nested": "^10.5.1", + "jss-plugin-props-sort": "^10.5.1", + "jss-plugin-rule-value-function": "^10.5.1", + "jss-plugin-vendor-prefixer": "^10.5.1", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/styles/node_modules/csstype": { + "version": "2.6.20", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", + "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" + }, + "node_modules/@material-ui/system": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", + "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.3", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/system/node_modules/csstype": { + "version": "2.6.20", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", + "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" + }, + "node_modules/@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/utils": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", + "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", + "dependencies": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@material-ui/utils/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/@mdi/font": { + "version": "5.9.55", + "resolved": "https://registry.npmjs.org/@mdi/font/-/font-5.9.55.tgz", + "integrity": "sha512-jswRF6q3eq8NWpWiqct6q+6Fg/I7nUhrxYJfiEM8JJpap0wVJLQdbKtyS65GdlK7S7Ytnx3TTi/bmw+tBhkGmg==" + }, + "node_modules/@mui/base": { + "version": "5.0.0-alpha.91", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.91.tgz", + "integrity": "sha512-/W5amPDz+Lout4FtX5HOyx2Q+YL/EtZciFrx2DDRuUm4M/pWnjfDZAtM+0aqimEvuk3FU+/PuFc7IAyhCSX4Cg==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@emotion/is-prop-valid": "^1.1.3", + "@mui/types": "^7.1.5", + "@mui/utils": "^5.9.1", + "@popperjs/core": "^2.11.5", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.9.2.tgz", + "integrity": "sha512-FItBuj9bPdVier2g5OBG2HHlQLou4JuH3gdnY43tpJOrCpmWrbDVJZqrSufKJFO00qjvTYaGlJedIu+vXn79qw==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@mui/base": "5.0.0-alpha.91", + "@mui/system": "^5.9.2", + "@mui/types": "^7.1.5", + "@mui/utils": "^5.9.1", + "@types/react-transition-group": "^4.4.5", + "clsx": "^1.2.1", + "csstype": "^3.1.0", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.9.1.tgz", + "integrity": "sha512-eIh2IZJInNTdgPLMo9cruzm8UDX5amBBxxsSoNre7lRj3wcsu3TG5OKjIbzkf4VxHHEhdPeNNQyt92k7L78u2A==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@mui/utils": "^5.9.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.8.7", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.8.7.tgz", + "integrity": "sha512-tVqtowjbYmiRq+qcqXK731L9eWoL9H8xTRhuTgaDGKdch1zlt4I2UwInUe1w2N9N/u3/jHsFbLcl1Un3uOwpQg==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@emotion/cache": "^11.9.3", + "csstype": "^3.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.9.2.tgz", + "integrity": "sha512-iOvt9tVeFapHL7f7M6BSIiKGMx6RTRvAmc8ipMnQ/MR5Qsxwnyv7qKtNC/K11Rk13Xx0VPaPAhyvBcsr3KdpHA==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@mui/private-theming": "^5.9.1", + "@mui/styled-engine": "^5.8.7", + "@mui/types": "^7.1.5", + "@mui/utils": "^5.9.1", + "clsx": "^1.2.1", + "csstype": "^3.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.5.tgz", + "integrity": "sha512-HnRXrxgHJYJcT8ZDdDCQIlqk0s0skOKD7eWs9mJgBUu70hyW4iA6Kiv3yspJR474RFH8hysKR65VVSzUSzkuwA==", + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.10.3", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.10.3.tgz", + "integrity": "sha512-4jXMDPfx6bpMVuheLaOpKTjpzw39ogAZLeaLj5+RJec3E37/hAZMYjURfblLfTWMMoGoqkY03mNsZaEwNobBow==", + "dependencies": { + "@babel/runtime": "^7.18.9", + "@types/prop-types": "^15.7.5", + "@types/react-is": "^16.7.1 || ^17.0.0", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/@mui/x-date-pickers": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.0.tgz", + "integrity": "sha512-LcWJYFC/SdYbu0JSz6pgQcCh/cvllyoAqfpqf6iTex7jMSEk8i008pCntyrEVVcjGrx0fakDduSJthlnf0xNxQ==", + "dependencies": { + "@babel/runtime": "^7.18.9", + "@date-io/core": "^2.15.0", + "@date-io/date-fns": "^2.15.0", + "@date-io/dayjs": "^2.15.0", + "@date-io/luxon": "^2.15.0", + "@date-io/moment": "^2.15.0", + "@mui/utils": "^5.9.3", + "@types/react-transition-group": "^4.4.5", + "clsx": "^1.2.1", + "prop-types": "^15.7.2", + "react-transition-group": "^4.4.5", + "rifm": "^0.12.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.4.1", + "@mui/system": "^5.4.1", + "date-fns": "^2.25.0", + "dayjs": "^1.10.7", + "luxon": "^1.28.0 || ^2.0.0 || ^3.0.0", + "moment": "^2.29.1", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz", + "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@puppeteer/replay": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@puppeteer/replay/-/replay-2.7.1.tgz", + "integrity": "sha512-w5lghKKto24XB7PGAyflkf1lSuFC+V4+UU5upJJkmWD+O0Q82H2PlHl/Wqwi9aQrngEkKSiK2xMK8Ycs5NddPA==", + "dependencies": { + "cli-table3": "0.6.3", + "colorette": "2.0.19", + "yargs": "17.6.2" + }, + "bin": { + "replay": "lib/cli.js" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "puppeteer": ">=18.0.5" + }, + "peerDependenciesMeta": { + "puppeteer": { + "optional": true + } + } + }, + "node_modules/@puppeteer/replay/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@puppeteer/replay/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@puppeteer/replay/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@puppeteer/replay/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@puppeteer/replay/node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/@puppeteer/replay/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/replay/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/replay/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/replay/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@puppeteer/replay/node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@puppeteer/replay/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", + "optional": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "node_modules/@types/react": { + "version": "17.0.48", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.48.tgz", + "integrity": "sha512-zJ6IYlJ8cYYxiJfUaZOQee4lh99mFihBoqkOSEGV+dFi9leROW6+PgstzQ+w3gWTnUfskALtQPGHK6dYmPj+2A==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-is": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", + "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.8.tgz", + "integrity": "sha512-2DK4YWKfgLnW9VDR9gnju1gcYRk3flKj8UNsms7fsRmFcg35slVTZEkqwBtX+wJBXaamFfn6NxSsZh3h12Ix/Q==", + "dependencies": { + "@babel/parser": "^7.18.4", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha512-GKp5tQ8h0KMPWIYGRHHXI1s5tUpZixZ3IHF2jAu42wSCf6In/G873s6/y4DdKdhWvzhu1T6mE1JgvnhAKqyYYQ==", + "dev": true, + "dependencies": { + "acorn": "^4.0.3" + } + }, + "node_modules/acorn-dynamic-import/node_modules/acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/align-text/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==", + "dev": true + }, + "node_modules/ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==", + "dev": true, + "dependencies": { + "string-width": "^2.0.0" + } + }, + "node_modules/ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha512-JoAxEa1DfP9m2xfB/y2r/aKcwXNlltr4+0QSBC4TrLfcxyvepX2Pv0t/xpgGV5bGsDzCYV8SzjWgyCW0T9yYbA==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "node_modules/are-you-es5": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/are-you-es5/-/are-you-es5-2.1.2.tgz", + "integrity": "sha512-gkT2bLCfzyJJr3lYoxZKScdD/Yp5zzghi+3KawONTvH/7rrgU3RMXYvGQnOTlqEFVgZFpEGj/6bZ6sF/9YtddA==", + "dependencies": { + "acorn": "^6.0.6", + "array-flatten": "^2.1.0", + "commander": "^2.19.0", + "find-up": "^4.1.0" + }, + "bin": { + "are-you-es5": "dist/cli.js" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "node_modules/array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==", + "dev": true + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", + "dev": true, + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha512-WKExI/eSGgGAkWAO+wMVdFObZV7hQen54UpD1kCCTN3tvlL3W1jL4+lPP/M7MwoP7Q4RHzKtO3JQ4HxYEcd+xQ==", + "dev": true, + "dependencies": { + "browserslist": "^1.7.6", + "caniuse-db": "^1.0.30000634", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^5.2.16", + "postcss-value-parser": "^3.2.3" + } + }, + "node_modules/autoprefixer/node_modules/browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha512-qHJblDE2bXVRYzuDetv/wAeHOJyO97+9wxC1cdCtyzgNuSozOyRCiiLaCR1f71AN66lQdVVBipWm63V+a7bPOw==", + "deprecated": "Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.", + "dev": true, + "dependencies": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + }, + "bin": { + "browserslist": "cli.js" + } + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "node_modules/babel-code-frame/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/babel-code-frame/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "dev": true + }, + "node_modules/babel-code-frame/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "dependencies": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "node_modules/babel-core/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/babel-core/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/babel-core/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/babel-eslint": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", + "integrity": "sha512-i2yKOhjgwUbUrJ8oJm6QqRzltIoFahGNPZ0HF22lUN4H1DW03JQyJm7WSv+I1LURQWjDNhVqFo04acYa07rhOQ==", + "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", + "dev": true, + "dependencies": { + "babel-code-frame": "^6.22.0", + "babel-traverse": "^6.23.1", + "babel-types": "^6.23.0", + "babylon": "^6.17.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "dependencies": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + } + }, + "node_modules/babel-generator/node_modules/jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha512-Mke0DA0QjUWuJlhsE0ZPPhYiJkRap642SmI/4ztCFaUs6V2AiH1sfecc+57NgaryfAA2VR3v6O+CSjC1jZJKOA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/babel-helper-bindify-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", + "integrity": "sha512-TYX2QQATKA6Wssp6j7jqlw4QLmABDN1olRdEHndYvBXdaXM5dcx6j5rN0+nd+aVL+Th40fAEYvvw/Xxd/LETuQ==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha512-gCtfYORSG1fUMX4kKraymq607FWgMWg+j42IFPc18kFQEsmtaibP4UrqsXt8FlEJle25HUd4tsoDR7H2wDhe9Q==", + "dev": true, + "dependencies": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha512-RL8n2NiEj+kKztlrVJM9JT1cXzzAdvWFh76xh/H1I4nKwunzE4INBXn8ieCZ+wh4zWszZk7NBS1s/8HR5jDkzQ==", + "dev": true, + "dependencies": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha512-bHkmjcC9lM1kmZcVpA5t2om2nzT/xiZpo6TJq7UlZ3wqKfzia4veeXbIhKvJXAMzhhEBd3cR1IElL5AenWEUpA==", + "dev": true, + "dependencies": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha512-qe5csbhbvq6ccry9G7tkXbzNtcDiH4r51rrPUbwwoTzZ18AqxWYRZT6AOmxrpxKnQBW0pYlBI/8vh73Z//78nQ==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-explode-class": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", + "integrity": "sha512-SFbWewr0/0U4AiRzsHqwsbOQeLXVa9T1ELdqEa2efcQB5KopTnunAqoj07TuHlN2lfTQNPGO/rJR4FMln5fVcA==", + "dev": true, + "dependencies": { + "babel-helper-bindify-decorators": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha512-Oo6+e2iX+o9eVvJ9Y5eKL5iryeRdsIkwRYheCuhYdVHsdEQysbc2z2QkqCLIYnNxkT5Ss3ggrHdXiDI7Dhrn4Q==", + "dev": true, + "dependencies": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha512-WfgKFX6swFB1jS2vo+DwivRN4NB8XUdM3ij0Y1gnC21y1tdBoe6xjVnd7NSI6alv+gZXCtJqvrTeMW3fR/c0ng==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha512-zAYl3tqerLItvG5cKYw7f1SpvIxS9zi7ohyGHaI9cgDUjAT6YcY9jIEH5CstetP5wHIVSceXwNS7Z5BpJg+rOw==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha512-Op9IhEaxhbRT8MDXx2iNuMgciu2V8lDvYCNQbDGjdBNCjaMvyLf4wl4A3b8IgndCyQF8TwfgsQ8T3VD8aX1/pA==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha512-VlPiWmqmGJp0x0oK27Out1D+71nVVCTSdlbhIVoaBAj2lUgrNjBCRR9+llO4lTSb2O4r7PJg+RobRkhBrf6ofg==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha512-RYqaPD0mQyQIFRu7Ho5wE2yvA/5jxqCIj/Lv4BXNq23mHYu/vxikOy2JueLiBxQknwapwrJeNCesvY0ZcfnlHg==", + "dev": true, + "dependencies": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha512-sLI+u7sXJh6+ToqDr57Bv973kCepItDhMou0xCP2YPVmR1jkHSCY+p1no8xErbV1Siz5QE8qKT1WIwybSWlqjw==", + "dev": true, + "dependencies": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha512-n7pFrqQm44TCYvrCDb0MqabAF+JUBq+ijBvNMUxpkLjJaAu32faIexewMumrH5KLLJ1HDyT0PTEqRyAe/GwwuQ==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha512-B1M5KBP29248dViEo1owyY32lk1ZSH2DaNNrXLGt8lyjjHm7pBqAdQ7VKUPR6EEDO323+OvT3MQXbCin8ooWdA==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", + "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.2", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha512-4Zp4unmHgw30A1eWI5EpACji2qMocisdXhAftfhXoSV9j0Tvj6nRFE3tOmRY912E0FMRm/L5xWE7MGVT2FoLnw==", + "dev": true + }, + "node_modules/babel-plugin-syntax-async-generators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "integrity": "sha512-EbciFN5Jb9iqU9bqaLmmFLx2G8pAUsvpWJ6OzOWBNrSY9qTohXj+7YfZx6Ug1Qqh7tCb1EA7Jvn9bMC1HBiucg==", + "dev": true + }, + "node_modules/babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha512-chI3Rt9T1AbrQD1s+vxw3KcwC9yHtF621/MacuItITfZX344uhQoANjpoSJZleAmW2tjlolqB/f+h7jIqXa7pA==", + "dev": true + }, + "node_modules/babel-plugin-syntax-decorators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "integrity": "sha512-AWj19x2aDm8qFQ5O2JcD6pwJDW1YdcnO+1b81t7gxrGjz5VHiUqeYWAR4h7zueWMalRelrQDXprv2FrY1dbpbw==", + "dev": true + }, + "node_modules/babel-plugin-syntax-dynamic-import": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "integrity": "sha512-MioUE+LfjCEz65Wf7Z/Rm4XCP5k2c+TbMd2Z2JKc7U9uwjBhAfNPE48KC4GTGKhppMeYVepwDBNO/nGY6NYHBA==", + "dev": true + }, + "node_modules/babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha512-Z/flU+T9ta0aIEKl1tGEmN/pZiI1uXmCiGFRegKacQfEJzp7iNsKloZmyJlQr+75FCJtiFfGIK03SiCvCt9cPQ==", + "dev": true + }, + "node_modules/babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha512-C4Aq+GaAj83pRQ0EFgTvw5YO6T3Qz2KGrNRwIj9mSoNHVvdZY4KO2uA6HNtNXCw993iSZnckY1aLW8nOi8i4+w==" + }, + "node_modules/babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha512-Gx9CH3Q/3GKbhs07Bszw5fPTlU+ygrOGfAhEt7W2JICwufpC4SuO0mG0+4NykPBSYPMJhqvVlDBU17qB1D+hMQ==", + "dev": true + }, + "node_modules/babel-plugin-transform-async-generator-functions": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", + "integrity": "sha512-uT7eovUxtXe8Q2ufcjRuJIOL0hg6VAUJhiWJBLxH/evYAw+aqoJLcYTR8hqx13iOx/FfbCMHgBmXWZjukbkyPg==", + "dev": true, + "dependencies": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-generators": "^6.5.0", + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha512-7BgYJujNCg0Ti3x0c/DL3tStvnKS6ktIYOmo9wginv/dfZOrbSZ+qG4IRRHMBOzZ5Awb1skTiAsQXg/+IWkZYw==", + "dev": true, + "dependencies": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha512-n4jtBA3OYBdvG5PRMKsMXJXHfLYw/ZOmtxCLOOwz6Ro5XlrColkStLnz1AS1L2yfPA9BKJ1ZNlmVCLjAL9DSIg==", + "dev": true, + "dependencies": { + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", + "integrity": "sha512-skQ2CImwDkCHu0mkWvCOlBCpBIHW4/49IZWVwV4A/EnWjL9bB6UBvLyMNe3Td5XDStSZNhe69j4bfEW8dvUbew==", + "dev": true, + "dependencies": { + "babel-helper-explode-class": "^6.24.1", + "babel-plugin-syntax-decorators": "^6.13.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha512-PCqwwzODXW7JMrzu+yZIaYbPQSKjDTAsNNlK2l5Gg9g4rz2VzLnZsStvp/3c46GfXpwkyufb3NCyG9+50FF1Vg==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha512-2+ujAT2UMBzYFm7tidUsYh+ZoIutxJ3pN9IYrF1/H6dCKtECfhmB8UkHVpyxDwkj0CYbQG35ykoz925TUnBc3A==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha512-YiN6sFAQ5lML8JjCmr7uerS5Yc/EMbgg9G8ZNmk2E3nYX4ckHR01wrkeeMijEf5WHNK5TW0Sl0Uu3pv3EdOJWw==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha512-5Dy7ZbRinGrNtmWpquZKZ3EGY8sDgIVB4CU8Om8q8tnMLrD/m94cKglVcHps0BCTdZ0TJeeAWOq2TK9MIY6cag==", + "dev": true, + "dependencies": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha512-C/uAv4ktFP/Hmh01gMTvYvICrKze0XVX9f2PdIXuriCSvUmV9j+u+BB9f5fJK3+878yMK6dkdcq+Ymr9mrcLzw==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha512-aNv/GDAW0j/f4Uy1OEPZn1mqD+Nfy9viFGBfQ5bZyT35YqOiqx7/tXdyfZkJ1sC21NyEsBdfDY6PYmLHF4r5iA==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha512-ossocTuPOssfxO2h+Z3/Ea1Vo1wWx31Uqy9vIiJusOP4TbF7tPs9U0sJ9pX9OJPf4lXRGj5+6Gkl/HHKiAP5ug==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha512-DLuRwoygCoXx+YfxHLkVx5/NpeSbVwfoTeBykpJK7JhYWlL/O8hgAK/reforUnZDlxasOrVPPJVI/guE3dCwkw==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha512-iFp5KIcorf11iBqu/y/a7DK3MN5di3pNCzto61FqCNnUX4qeBwcV1SLqe10oXNnCaxBUImX3SckX2/o1nsrTcg==", + "dev": true, + "dependencies": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha512-tjFl0cwMPpDYyoqYA9li1/7mGFit39XiNX5DKC/uCNjBctMxyL1/PT/l4rSlbvBG1pOKI88STRdUsWXB3/Q9hQ==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha512-LnIIdGWIKdw7zwckqx+eGjcS8/cl8D74A3BpJbGjKTFFNJSMrjN4bIh22HY1AlkUbeLG6X6OZj56BDvWD+OeFA==", + "dev": true, + "dependencies": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "dependencies": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "node_modules/babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha512-ONFIPsq8y4bls5PPsAWYXH/21Hqv64TBxdje0FvU3MhIV6QM2j5YS7KvAzg/nTIVLot2D2fmFQrFWCbgHlFEjg==", + "dev": true, + "dependencies": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha512-LpVbiT9CLsuAIp3IG0tfbVo81QIhn6pE8xBJ7XSeCtFlMltuar5VuBV6y6Q45tpui9QWcy5i0vLQfCfrnF7Kiw==", + "dev": true, + "dependencies": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha512-8G5hpZMecb53vpD3mjs64NhI1au24TAmokQ4B+TBFBjN9cVoGoOvotdrMMRmHvVZUEvqGUPWL514woru1ChZMA==", + "dev": true, + "dependencies": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha512-8HxlW+BB5HqniD+nLkQ4xSAVq3bR/pcYW9IigY+2y0dI+Y7INFeTbfAQr+63T3E4UDsZGjyb+l9txUnABWxlOQ==", + "dev": true, + "dependencies": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha512-mDdocSfUVm1/7Jw/FIRNw9vPrBQNePy6wZJlR8HAUBLybNp1w/6lr6zZ2pjMShee65t/ybR5pT8ulkLzD1xwiw==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha512-3Ghhi26r4l3d0Js933E5+IhHwk0A1yiutj9gwvzmFbVV0sPMYk2lekhOufHBswX7NCoSeF4Xrl3sCIuSIa+zOg==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha512-CYP359ADryTo3pCsH0oxRo/0yn6UsEZLqYohHmvLQdfS9xkf+MbCzE3/Kolw9OYIY4ZMilH25z/5CbQbwDD+lQ==", + "dev": true, + "dependencies": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha512-x8b9W0ngnKzDMHimVtTfn5ryimars1ByTqsfBDwAqLibmuuQY6pgBQi5z1ErIsUOWBdw1bW9FSz5RZUojM4apg==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha512-fz6J2Sf4gYN6gWgRZaoFXmq93X+Li/8vf+fb0sGDVtdeWvxC9y5/bTD7bvfWMEq6zetGEHpWjtzRGSugt5kNqw==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha512-v61Dbbihf5XxnYjtBN04B/JBvsScY37R1cZT5r9permN1cp+b70DY3Ib3fIkgn1DI9U3tGgBJZVD8p/mE/4JbQ==", + "dev": true, + "dependencies": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "node_modules/babel-plugin-transform-es2015-unicode-regex/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/babel-plugin-transform-es2015-unicode-regex/node_modules/regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha512-tJ9+S4oKjxY8IZ9jmjnp/mtytu1u3iyIQAfmI51IKWH6bFf7XR1ybtaO6j7INhZKXOTYADk7V5qxaqLkmNxiZQ==", + "dev": true, + "dependencies": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "node_modules/babel-plugin-transform-es2015-unicode-regex/node_modules/regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha512-x+Y3yA24uF68m5GA+tBjbGYo64xXVJpbToBaWCoSNSc1hdk6dfctaRWrNFTVJZIIhL5GxW8zwjoixbnifnK59g==", + "dev": true + }, + "node_modules/babel-plugin-transform-es2015-unicode-regex/node_modules/regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha512-jlQ9gYLfk2p3V5Ag5fYhA7fv7OHzd1KUH0PRP46xc3TgwjwgROIW572AfYg/X9kaNq/LJnu6oJcFRXlIrGoTRw==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha512-LzXDmbMkklvNhprr20//RStKVcT8Cu+SQtX18eMHLhjHf2yFzwtQ0S2f0jQ+89rokoNdmwoSqYzAhq86FxlLSQ==", + "dev": true, + "dependencies": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha512-ocgA9VJvyxwt+qJB0ncxV8kb/CjfTcECUY4tQ5VT7nP6Aohzobm8CDFaQ5FHdvZQzLmf0sgDxB8iRXZXxwZcyA==", + "dependencies": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "node_modules/babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha512-LS+dBkUGlNR15/5WHKe/8Neawx663qttS6AGqoOUhICc9d1KciBvtrQSuc0PI+CxQ2Q/S1aKuJ+u64GtLdcEZg==", + "dev": true, + "dependencies": { + "regenerator-transform": "^0.10.0" + } + }, + "node_modules/babel-plugin-transform-regenerator/node_modules/regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "node_modules/babel-plugin-transform-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", + "integrity": "sha512-cpGMVC1vt/772y3jx1gwSaTitQVZuFDlllgreMsZ+rTYC6jlYXRyf5FQOgSnckOiA5QmzbXTyBY2A5AmZXF1fA==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha512-j3KtSpjyLSJxNoCDrhwiJad8kw0gJ9REGj8/CqL0HeRyLnvUNYV9zcqluL6QJSXh3nfsLEmSLvwRfGzrgR96Pw==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha512-F2rZGQnAdaHWQ8YAoeRbukc7HS9QgdgeyJ0rQDd485v9opwuPvjpPFcOOT/WmkKTdgy9ESgSPXDcTNpzrGr6iQ==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" + } + }, + "node_modules/babel-polyfill/node_modules/regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==", + "dev": true + }, + "node_modules/babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha512-XfwUqG1Ry6R43m4Wfob+vHbIVBIqTg/TJY4Snku1iIzeH7mUnwHA8Vagmv+ZQbPwhS8HgsdQvy28Py3k5zpoFQ==", + "deprecated": "🙌 Thanks for using Babel: we recommend using babel-preset-env now: please read https://babeljs.io/env to update!", + "dev": true, + "dependencies": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, + "node_modules/babel-preset-stage-2": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", + "integrity": "sha512-9F+nquz+37PrlTSBdpeQBKnQfAMNBnryXw+m4qBh35FNbJPfzZz+sjN2G5Uf1CRedU9PH7fJkTbYijxmkLX8Og==", + "dev": true, + "dependencies": { + "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-decorators": "^6.24.1", + "babel-preset-stage-3": "^6.24.1" + } + }, + "node_modules/babel-preset-stage-3": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", + "integrity": "sha512-eCbEOF8uN0KypFXJmZXn2sTk7bPV9uM5xov7G/7BM08TbQEObsVs0cEWfy6NQySlfk7JBi/t+XJP1JkruYfthA==", + "dev": true, + "dependencies": { + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-generator-functions": "^6.24.1", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-exponentiation-operator": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.22.0" + } + }, + "node_modules/babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha512-veliHlHX06wjaeY8xNITbveXSiI+ASFnOqvne/LaIJIqOWi2Ogmj91KOugEz/hoh/fwMhXNBJPCv8Xaz5CyM4A==", + "dev": true, + "dependencies": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + } + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/babel-runtime/node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "node_modules/babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==", + "dev": true, + "dependencies": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-traverse/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/babel-traverse/node_modules/globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-traverse/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "node_modules/babel-types/node_modules/to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true, + "bin": { + "babylon": "bin/babylon.js" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, + "dependencies": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-sign/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "node_modules/buffer/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "node_modules/cacache/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==", + "dev": true, + "dependencies": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha512-SBTl70K0PkDUIebbkXrxWqZlHNs0wRgRD6QZ8guctShjbh63gEPfF+Wj0Yw+75f5Y8tSzqAI/NcisYv/cCah2Q==", + "dev": true, + "dependencies": { + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-api/node_modules/browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha512-qHJblDE2bXVRYzuDetv/wAeHOJyO97+9wxC1cdCtyzgNuSozOyRCiiLaCR1f71AN66lQdVVBipWm63V+a7bPOw==", + "deprecated": "Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.", + "dev": true, + "dependencies": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + }, + "bin": { + "browserslist": "cli.js" + } + }, + "node_modules/caniuse-db": { + "version": "1.0.30001373", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001373.tgz", + "integrity": "sha512-NOoFLQ0w7geqot8ENHEE/cRqQN0HdVtJeG2h+2cjmEYb07X0HGwBQxREKWpt5YUhNPmAxHKVGPbak1FLey6GGw==", + "dev": true + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001372", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001372.tgz", + "integrity": "sha512-tBgBhIXBIqORB9ieUEYBKRfSlaF6YPq7WNNqcreF6Cl24UKNGIvE5/rP59dOGN6TRIS/5zOMHAMSRrVNmifWWw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/capture-stack-trace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==", + "dev": true, + "dependencies": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/clap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", + "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", + "dev": true, + "dependencies": { + "chalk": "^1.1.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/clap/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/classcat": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.3.tgz", + "integrity": "sha512-6dK2ke4VEJZOFx2ZfdDAl5OhEL8lvkl6EHF92IfRePfHxQTqir5NlcNVUv+2idjDqCX2NDc8m8YSAI5NI975ZQ==" + }, + "node_modules/cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha512-KAGck/eNAmCL0dcT3BiuYwLbExK6lduR8DxM3C1TyDzaXhZHyZ8ooX5I5+na2e3dPFuibfxrGdorr0/Lr7RYCQ==", + "dev": true, + "dependencies": { + "q": "^1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha512-Ajpjd8asqZ6EdxQeqGzU5WBhhTfJ/0cA4Wlbre7e5vXfmDSmda7Ov6jeKoru+b0vHcb1CqvuroTHp5zIWzhVMA==", + "dev": true, + "dependencies": { + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha512-sz29j1bmSDfoAxKIEU6zwoIZXN6BrFbAMIhfYCNyiZXBDuU/aiHlN84lp/xDzL2ubyFhLDobHIlU1X70XRrMDA==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "node_modules/colormin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha512-XSEQUUQUR/lXqGyddiNH3XYFUPYlYr1vXy9rTFMsSOw+J7Q6EQkdlQIrTlYn4TccpsOaUE1PYQNjBn20gwCdgQ==", + "dev": true, + "dependencies": { + "color": "^0.11.0", + "css-color-names": "0.0.4", + "has": "^1.0.1" + } + }, + "node_modules/colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/configstore": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", + "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", + "dev": true, + "dependencies": { + "dot-prop": "^4.2.1", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/configstore/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/configstore/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "node_modules/consolidate": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.14.5.tgz", + "integrity": "sha512-PZFskfj64QnpKVK9cPdY36pyWEhZNM+srRVqtwMiVTlnViSoZcvX35PpBhhUcyLTHXYvz7pZRmxvsqwzJqg9kA==", + "dev": true, + "dependencies": { + "bluebird": "^3.1.1" + } + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "dependencies": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "node_modules/copy-concurrently/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/core-js-compat": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.0.tgz", + "integrity": "sha512-F+2E63X3ff/nj8uIrf8Rf24UDGIz7p838+xjEp+Bx3y8OWXj+VTPPZNCtdqovPaS9o7Tka5mCH01Zn5vOd6UQg==", + "dependencies": { + "browserslist": "^4.21.2", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", + "dev": true, + "dependencies": { + "capture-stack-trace": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-env": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", + "integrity": "sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.5" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dependencies": { + "node-fetch": "2.6.7" + } + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/css-loader": { + "version": "0.28.11", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", + "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", + "dev": true, + "dependencies": { + "babel-code-frame": "^6.26.0", + "css-selector-tokenizer": "^0.7.0", + "cssnano": "^3.10.0", + "icss-utils": "^2.1.0", + "loader-utils": "^1.0.2", + "lodash.camelcase": "^4.3.0", + "object-assign": "^4.1.1", + "postcss": "^5.0.6", + "postcss-modules-extract-imports": "^1.2.0", + "postcss-modules-local-by-default": "^1.2.0", + "postcss-modules-scope": "^1.1.0", + "postcss-modules-values": "^1.3.0", + "postcss-value-parser": "^3.3.0", + "source-list-map": "^2.0.0" + }, + "engines": { + "node": ">=0.12.0 || >= 4.3.0 < 5.0.0 || >=5.10" + } + }, + "node_modules/css-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/css-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "node_modules/css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dependencies": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha512-0o0IMQE0Ezo4b41Yrm8U6Rp9/Ag81vNXY1gZMnT1XhO4DpjEf2utKERqWJbOoz3g1Wdc1d3QSta/cIuJ1wSTEg==", + "dev": true, + "dependencies": { + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" + } + }, + "node_modules/csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha512-FmCI/hmqDeHHLaIQckMhMZneS84yzUZdrWDAvJVVxOwcKE1P1LF9FGmzr1ktIQSxOw6fl3PaQsmfg+GN+VvR3w==", + "dev": true, + "dependencies": { + "clap": "^1.0.9", + "source-map": "^0.5.3" + }, + "bin": { + "csso": "bin/csso" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/csstype": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", + "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==", + "dev": true + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dayjs": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", + "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "peer": true + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==", + "dev": true + }, + "node_modules/del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha512-7yjqSoVSlJzA4t/VUwazuEagGeANEKB3f/aNI//06pfKgwoCb7f6Q1gETN1sZzYaj6chTQ0AhIwDiPdfOjko4A==", + "dev": true, + "dependencies": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/del/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/devtools-protocol": { + "version": "0.0.1068969", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1068969.tgz", + "integrity": "sha512-ATFTrPbY1dKYhPPvpjtwWKSK2mIwGmRwX54UASn9THEuIZCe2n9k3vVuMmt6jWeL+e5QaaguEv/pMyR+JQB7VQ==" + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", + "dev": true, + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dom-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/dom-urls/-/dom-urls-1.1.0.tgz", + "integrity": "sha512-LNxCeExaNbczqMVfQUyLdd+r+smG7ixIa+doeyiJ7nTmL8aZRrJhHkEYBEYVGvYv7k2DOEBh2eKthoCmWpfICg==", + "dev": true, + "dependencies": { + "urijs": "^1.16.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/dot-prop": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", + "dev": true, + "dependencies": { + "is-obj": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/duplexer3": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "dev": true + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.204", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.204.tgz", + "integrity": "sha512-5Ojjtw9/c9HCXtMVE6SXVSHSNjmbFOXpKprl6mY/5moLSxLeWatuYA7KTD+RzJMxLRH6yNNQrqGz9p6IoNBMgw==" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", + "integrity": "sha512-ZaAux1rigq1e2nQrztHn4h2ugvpzZxs64qneNah+8Mh/K0CRqJFJc+UoXnUsq+1yX+DmQFPPdVqboKAJ89e0Iw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.7" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.61", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", + "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "node_modules/es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha512-7S8YXIcUfPMOr3rqJBVMePAbRsD1nWeSMQ86K/lDI76S3WKXz+KWILvTIPbTroubOkZTGh+b+7/xIIphZXNYbA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + } + }, + "node_modules/es6-set/node_modules/es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha512-exfuQY8UGtn/N+gL1iKkH8fpNd5sJ760nJq6mmZAHldfxMD5kX07lbQuYlspoXsuknXNv9Fb7y2GsPOnQIbxHg==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha512-75IUQsusDdalQEW/G/2esa87J7raqdJF+Ca0/Xm5C3Q58Nr4yVYjZGp/P1+2xiEVgXRrA39dpRb8LcshajbqDQ==", + "dev": true, + "dependencies": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/eslint": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.20.0.tgz", + "integrity": "sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA==", + "peer": true, + "dependencies": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "peer": true + }, + "node_modules/eslint/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "peer": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "peer": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/espree": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "peer": true, + "dependencies": { + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "integrity": "sha512-bbB5tEuvC+SuRUG64X8ghvjgiRniuA4WlehWbFnoN4z6TxDXpyX+BMHF7rMgZAqoe+EbyNRUbHN0uuP9phy5jQ==", + "dev": true, + "dependencies": { + "original": ">=0.0.5" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "dev": true, + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "dev": true, + "dependencies": { + "type": "^2.5.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", + "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/extract-zip/node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "peer": true + }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fiber": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fiber/-/fiber-1.0.4.tgz", + "integrity": "sha512-TASLDBU4gPNnv1oSMfvJtOJ0yN1k3s8KMzE7jrbCZo6xIEapMURT+Cay2xG5WBWfhay64D99G//dIlXj2PGXiA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "peer": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "dev": true, + "dependencies": { + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" + }, + "engines": { + "node": ">= 4.3 < 5.0.0 || >= 5.10" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/file-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/file-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "peer": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "peer": true + }, + "node_modules/flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", + "deprecated": "flatten is deprecated in favor of utility frameworks such as lodash.", + "dev": true + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "peer": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "node_modules/get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, + "dependencies": { + "ini": "^1.3.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", + "dev": true, + "dependencies": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/highcharts": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-9.3.3.tgz", + "integrity": "sha512-QeOvm6cifeZYYdTLm4IxZsXcOE9c4xqfs0z0OJJ0z7hhA9WG0rmcVAyuIp5HBl/znjA/ayYHmpYjBYD/9PG4Fg==" + }, + "node_modules/highcharts-vue": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/highcharts-vue/-/highcharts-vue-1.4.0.tgz", + "integrity": "sha512-fTPqJmU/wR/nUEtofbC/iJqcgULGcP3C0Hd4YYCKH2FiiteNs7UidjSM4eYj5Y4ldpOVhwPtNj/m6XwC3ajp6g==", + "peerDependencies": { + "highcharts": ">=5.0.0", + "vue": ">=1.0.0" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha512-ycURW7oUxE2sNiPVw1HVEFsW+ecOpJ5zaj7eC0RlwhibhRBod20muUN8qu/gzx956YrLolVvs1MTXwKgC2rVEg==", + "dev": true, + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "dev": true + }, + "node_modules/html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", + "dev": true + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz", + "integrity": "sha512-aYk1rTKqLTus23X3L96LGNCGNgWpG4cG0XoZIT1GUPhhulEHX/QalnO6Vbo+WmKWi4AL2IidjuC0wZtbpg0yhQ==", + "dev": true, + "dependencies": { + "http-proxy": "^1.18.1", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==", + "dev": true + }, + "node_modules/icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha512-bsVoyn/1V4R1kYYjLcWLedozAM4FClZUdjE9nIr8uWY7xs78y9DATgwz2wGU7M+7z55KenmmTkN2DVJ7bqzjAA==", + "dev": true, + "dependencies": { + "postcss": "^6.0.1" + } + }, + "node_modules/icss-utils/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/icss-utils/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==", + "dev": true + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "dev": true, + "dependencies": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==", + "dev": true, + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/internal-ip": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", + "integrity": "sha512-DzGfTasXPmwizQP4XV2rR6r2vp8TjlOpMnJqG9Iy2i1pl1lkZdZj5rSpIc7YFGX2nS46PPgAGEyT+Q5hE2FB2g==", + "dev": true, + "dependencies": { + "meow": "^3.3.0" + }, + "bin": { + "internal-ip": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "dependencies": { + "ci-info": "^1.5.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" + }, + "node_modules/is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==", + "dev": true, + "dependencies": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha512-Ya1giYJUkcL/94quj0+XGcmts6cETPBW1MiFz1ReJrnDJ680F52qpAEGAEGU0nq96FRGIGPx6Yo1CyPXcOoyGw==", + "dev": true, + "dependencies": { + "html-comment-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "peer": true + }, + "node_modules/json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jss": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.9.2.tgz", + "integrity": "sha512-b8G6rWpYLR4teTUbGd4I4EsnWjg7MN0Q5bSsjKhVkJVjhQDy2KzkbD2AW3TuT0RYZVmZZHKIrXDn6kjU14qkUg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/jss" + } + }, + "node_modules/jss-plugin-camel-case": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.2.tgz", + "integrity": "sha512-wgBPlL3WS0WDJ1lPJcgjux/SHnDuu7opmgQKSraKs4z8dCCyYMx9IDPFKBXQ8Q5dVYij1FFV0WdxyhuOOAXuTg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.9.2" + } + }, + "node_modules/jss-plugin-default-unit": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.2.tgz", + "integrity": "sha512-pYg0QX3bBEFtTnmeSI3l7ad1vtHU42YEEpgW7pmIh+9pkWNWb5dwS/4onSfAaI0kq+dOZHzz4dWe+8vWnanoSg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.9.2" + } + }, + "node_modules/jss-plugin-global": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.2.tgz", + "integrity": "sha512-GcX0aE8Ef6AtlasVrafg1DItlL/tWHoC4cGir4r3gegbWwF5ZOBYhx04gurPvWHC8F873aEGqge7C17xpwmp2g==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.9.2" + } + }, + "node_modules/jss-plugin-nested": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.2.tgz", + "integrity": "sha512-VgiOWIC6bvgDaAL97XCxGD0BxOKM0K0zeB/ECyNaVF6FqvdGB9KBBWRdy2STYAss4VVA7i5TbxFZN+WSX1kfQA==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.9.2", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-props-sort": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.2.tgz", + "integrity": "sha512-AP1AyUTbi2szylgr+O0OB7gkIxEGzySLITZ2GpsaoX72YMCGI2jYAc+WUhPfvUnZYiauF4zTnN4V4TGuvFjJlw==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.9.2" + } + }, + "node_modules/jss-plugin-rule-value-function": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.2.tgz", + "integrity": "sha512-vf5ms8zvLFMub6swbNxvzsurHfUZ5Shy5aJB2gIpY6WNA3uLinEcxYyraQXItRHi5ivXGqYciFDRM2ZoVoRZ4Q==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.9.2", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-vendor-prefixer": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.2.tgz", + "integrity": "sha512-SxcEoH+Rttf9fEv6KkiPzLdXRmI6waOTcMkbbEFgdZLDYNIP9UKNHFy6thhbRKqv0XMQZdrEsbDyV464zE/dUA==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.9.2" + } + }, + "node_modules/killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==", + "dev": true, + "dependencies": { + "package-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "dev": true, + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "peer": true + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", + "dev": true, + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-expression-evaluator": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.4.0.tgz", + "integrity": "sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==", + "dev": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha512-nOBDrc/wgpkd3X/JOhMqYR+/eLqlfLP4oQfoBA6QExIxEl+GU01oyEkwWyueyO8110pUKijtiHGhEmYoOn88oQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==", + "dev": true, + "dependencies": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "dependencies": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", + "dev": true, + "dependencies": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "node_modules/move-concurrently/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", + "dev": true + }, + "node_modules/nan": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", + "dev": true, + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "peer": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==", + "dev": true, + "dependencies": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "dev": true + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "peer": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/orderedmap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.0.0.tgz", + "integrity": "sha512-buf4PoAMlh45b8a8gsGy/X6w279TSqkyAS0C0wdTSJwFSU+ljQFJON5I8NfjLHoCXwpSROIo2wr0g33T+kQshQ==" + }, + "node_modules/original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "dependencies": { + "url-parse": "^1.4.3" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "dependencies": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==", + "dev": true, + "dependencies": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/package-json/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "dependencies": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" + }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha512-iBcptYFq+QUh9gzP7ta2btw50o40s4uLI4UDVgd5yRAZtUDWc5APdl5yQDd2h/TyiZNbJrv0HiYhT102CMgN7Q==", + "dev": true, + "dependencies": { + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" + } + }, + "node_modules/postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha512-XXitQe+jNNPf+vxvQXIQ1+pvdQKWKgkx8zlJNltcMEmLma1ypDRDQwlLt+6cP26fBreihNhZxohh1rcgCH2W5w==", + "dev": true, + "dependencies": { + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" + } + }, + "node_modules/postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha512-SE7mf25D3ORUEXpu3WUqQqy0nCbMuM5BEny+ULE/FXdS/0UMA58OdzwvzuHJRpIFlk1uojt16JhaEogtP6W2oA==", + "dev": true, + "dependencies": { + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" + } + }, + "node_modules/postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha512-yGbyBDo5FxsImE90LD8C87vgnNlweQkODMkUZlDVM/CBgLr9C5RasLGJxxh9GjVOBeG8NcCMatoqI1pXg8JNXg==", + "dev": true, + "dependencies": { + "postcss": "^5.0.14" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha512-+lk5W1uqO8qIUTET+UETgj9GWykLC3LOldr7EehmymV0Wu36kyoHimC4cILrAAYpHQ+fr4ypKcWcVNaGzm0reA==", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + } + }, + "node_modules/postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha512-IBFoyrwk52dhF+5z/ZAbzq5Jy7Wq0aLUsOn69JNS+7YeuyHaNzJwBIYE0QlUH/p5d3L+OON72Fsexyb7OK/3og==", + "dev": true, + "dependencies": { + "postcss": "^5.0.14" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha512-IyKoDL8QNObOiUc6eBw8kMxBHCfxUaERYTUe2QF8k7j/xiirayDzzkmlR6lMQjrAM1p1DDRTvWrS7Aa8lp6/uA==", + "dev": true, + "dependencies": { + "postcss": "^5.0.16" + } + }, + "node_modules/postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha512-nCbFNfqYAbKCw9J6PSJubpN9asnrwVLkRDFc4KCwyUEdOtM5XDE/eTW3OpqHrYY1L4fZxgan7LLRAAYYBzwzrg==", + "dev": true, + "dependencies": { + "postcss": "^5.0.14", + "uniqs": "^2.0.0" + } + }, + "node_modules/postcss-filter-plugins": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", + "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + } + }, + "node_modules/postcss-load-config": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", + "integrity": "sha512-3fpCfnXo9Qd/O/q/XL4cJUhRsqjVD2V1Vhy3wOEcLE5kz0TGtdDXJSoiTdH4e847KphbEac4+EZSH4qLRYIgLw==", + "dev": true, + "dependencies": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0", + "postcss-load-options": "^1.2.0", + "postcss-load-plugins": "^2.3.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-load-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/postcss-load-config/node_modules/cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "dev": true, + "dependencies": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-load-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/postcss-load-config/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-load-options": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", + "integrity": "sha512-WKS5LJMZLWGwtfhs5ahb2ycpoYF3m0kK4QEaM+elr5EpiMt0H296P/9ETa13WXzjPwB0DDTBiUBBWSHoApQIJg==", + "dev": true, + "dependencies": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-load-options/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/postcss-load-options/node_modules/cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "dev": true, + "dependencies": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-load-options/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/postcss-load-options/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-load-plugins": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", + "integrity": "sha512-/WGUMYhKiryWjYO6c7kAcqMuD7DVkaQ8HcbQenDme/d3OBOmrYMFObOKgUWyUy1uih5U2Dakq8H6VcJi5C9wHQ==", + "dev": true, + "dependencies": { + "cosmiconfig": "^2.1.1", + "object-assign": "^4.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-load-plugins/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/postcss-load-plugins/node_modules/cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "dev": true, + "dependencies": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-load-plugins/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/postcss-load-plugins/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha512-9DHmfCZ7/hNHhIKnNkz4CU0ejtGen5BbTRJc13Z2uHfCedeCUsK2WEQoAJRBL+phs68iWK6Qf8Jze71anuysWA==", + "dev": true, + "dependencies": { + "has": "^1.0.1", + "postcss": "^5.0.10", + "postcss-value-parser": "^3.1.1" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha512-ma7YvxjdLQdifnc1HFsW/AW6fVfubGyR+X4bE3FOSdBVMY9bZjKVdklHT+odknKBB7FSCfKIHC3yHK7RUAqRPg==", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + } + }, + "node_modules/postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha512-Wgg2FS6W3AYBl+5L9poL6ZUISi5YzL+sDCJfM7zNw/Q1qsyVQXXZ2cbVui6mu2cYJpt1hOKCGj1xA4mq/obz/Q==", + "dev": true, + "dependencies": { + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha512-qHJblDE2bXVRYzuDetv/wAeHOJyO97+9wxC1cdCtyzgNuSozOyRCiiLaCR1f71AN66lQdVVBipWm63V+a7bPOw==", + "deprecated": "Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.", + "dev": true, + "dependencies": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + }, + "bin": { + "browserslist": "cli.js" + } + }, + "node_modules/postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha512-tPLZzVAiIJp46TBbpXtrUAKqedXSyW5xDEo1sikrfEfnTs+49SBZR/xDdqCiJvSSbtr615xDsaMF3RrxS2jZlA==", + "dev": true + }, + "node_modules/postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha512-vFSPzrJhNe6/8McOLU13XIsERohBJiIFFuC1PolgajOZdRWqRgKITP/A4Z/n4GQhEmtbxmO9NDw3QLaFfE1dFQ==", + "dev": true, + "dependencies": { + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha512-DZhT0OE+RbVqVyGsTIKx84rU/5cury1jmwPa19bViqYPQu499ZU831yMzzsyC8EhiZVd73+h5Z9xb/DdaBpw7Q==", + "dev": true, + "dependencies": { + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha512-hhJdMVgP8vasrHbkKAk+ab28vEmPYgyuDzRl31V3BEB3QOR3L5TTIVEWLDNnZZ3+fiTi9d6Ker8GM8S1h8p2Ow==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha512-e13vxPBSo3ZaPne43KVgM+UETkx3Bs4/Qvm6yXI9HQpQp4nyb7HZ0gKpkF+Wn2x+/dbQ+swNpCdZSbMOT7+TIA==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", + "dev": true, + "dependencies": { + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-extract-imports/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-extract-imports/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==", + "dev": true, + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==", + "dev": true, + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-scope/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-scope/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA==", + "dev": true, + "dependencies": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-values/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-modules-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha512-RKgjEks83l8w4yEhztOwNZ+nLSrJ+NvPNhpS+mVDzoaiRHZQVoG7NF2TP5qjwnaN9YswUhj6m1E0S0Z+WDCgEQ==", + "dev": true, + "dependencies": { + "postcss": "^5.0.5" + } + }, + "node_modules/postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha512-WqtWG6GV2nELsQEFES0RzfL2ebVwmGl/M8VmMbshKto/UClBo+mznX8Zi4/hkThdqx7ijwv+O8HWPdpK7nH/Ig==", + "dev": true, + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" + } + }, + "node_modules/postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha512-5RB1IUZhkxDCfa5fx/ogp/A82mtq+r7USqS+7zt0e428HJ7+BHCxyeY39ClmkkUtxdOd3mk8gD6d9bjH2BECMg==", + "dev": true, + "dependencies": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" + } + }, + "node_modules/postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha512-0+Ow9e8JLtffjumJJFPqvN4qAvokVbdQPnijUDSOX8tfTwrILLP4ETvrZcXZxAtpFLh/U0c+q8oRMJLr1Kiu4w==", + "dev": true, + "dependencies": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha512-jJFrV1vWOPCQsIVitawGesRgMgunbclERQ/IRGW7r93uHrVzNQQmHQ7znsOIjJPZ4yWMzs5A8NFhp3AkPHPbDA==", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha512-lGgRqnSuAR5i5uUg1TA33r9UngfTadWxOyL2qx1KuPoCQzfmtaHjp9PuwX7yVyRxG3BWBzeFUaS5uV9eVgnEgQ==", + "dev": true, + "dependencies": { + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" + } + }, + "node_modules/postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha512-3pqyakeGhrO0BQ5+/tGTfvi5IAUAhHRayGK8WFSu06aEv2BmHoXw/Mhb+w7VY5HERIuC+QoUI7wgrCcq2hqCVA==", + "dev": true, + "dependencies": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "node_modules/postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha512-y5AdQdgBoF4rbpdbeWAJuxE953g/ylRfVNp6mvAi61VCN/Y25Tu9p5mh3CyI42WbTRIiwR9a1GdFtmDnNPeskQ==", + "dev": true, + "dependencies": { + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha512-WZX8r1M0+IyljoJOJleg3kYm10hxNYF9scqAT7v/xeSX1IdehutOM85SNO0gP9K+bgs86XERr7Ud5u3ch4+D8g==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha512-uhRZ2hRgj0lorxm9cr62B01YzpUe63h0RXMXQ4gWW3oa2rpJh+FJAiEAytaFCPU/VgaBS+uW2SJ1XKyDNz1h4w==", + "dev": true, + "dependencies": { + "has": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "node_modules/postcss/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pretty-bytes": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", + "integrity": "sha512-yJAF+AjbHKlxQ8eezMd/34Mnj/YTQ3i6kLzvVsH4l/BfIFtp444n0wVbnsn66JimZ9uBofv815aRp1zCppxlWw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/prosemirror-collab": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.0.tgz", + "integrity": "sha512-+S/IJ69G2cUu2IM5b3PBekuxs94HO1CxJIWOFrLQXUaUDKL/JfBx+QcH31ldBlBXyDEUl+k3Vltfi1E1MKp2mA==", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.3.0.tgz", + "integrity": "sha512-BwBbZ5OAScPcm0x7H8SPbqjuEJnCU2RJT9LDyOiiIl/3NbL1nJZI4SFNHwU2e/tRr2Xe7JsptpzseqvZvToLBQ==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.5.0.tgz", + "integrity": "sha512-vy7i77ddKyXlu8kKBB3nlxLBnsWyKUmQIPB5x8RkYNh01QNp/qqGmdd5yZefJs0s3rtv5r7Izfu2qbtr+tYAMQ==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.1.tgz", + "integrity": "sha512-GKTeE7ZoMsx5uVfc51/ouwMFPq0o8YrZ7Hx4jTF4EeGbXxBveUV8CGv46mSHuBBeXGmvu50guoV2kSnOeZZnUA==", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.3.0.tgz", + "integrity": "sha512-qo/9Wn4B/Bq89/YD+eNWFbAytu6dmIM85EhID+fz9Jcl9+DfGEo8TTSrRhP15+fFEoaPqpHSxlvSzSEbmlxlUA==", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.2.0.tgz", + "integrity": "sha512-eAW/M/NTSSzpCOxfR8Abw6OagdG0MiDAiWHQMQveIsZtoKVYzm0AflSPq/ymqJd56/Su1YPbwy9lM13wgHOFmQ==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz", + "integrity": "sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.1.tgz", + "integrity": "sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.1.tgz", + "integrity": "sha512-rYT4azRBZboxl54a4dRSiW0wXBEIZcMCCM9z9x0TD1jqJMm89GR16UgPNYb5+pKZ8qyti5enYN1Hhztq3KvqrQ==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz", + "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.2.4.tgz", + "integrity": "sha512-0hvb436mgaOv9pyIbuL3ZbD/qCSFO9tl353yax1WMJfmgUX5iy13COWbj7x+vhUF6SmufA6kgSZz+MTsy2sJ+w==", + "dependencies": { + "prosemirror-keymap": "^1.1.2", + "prosemirror-model": "^1.8.1", + "prosemirror-state": "^1.3.1", + "prosemirror-transform": "^1.2.1", + "prosemirror-view": "^1.13.3" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.6.0.tgz", + "integrity": "sha512-MAp7AjsjEGEqQY0sSMufNIUuEyB1ZR9Fqlm8dTwwWwpEJRv/plsKjWXBbx52q3Ml8MtaMcd7ic14zAHVB3WaMw==", + "dependencies": { + "prosemirror-model": "^1.0.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.27.0.tgz", + "integrity": "sha512-yNCQW5eiPkrMgjOT5Xa/ItIvcM7JBG7ikZKaHo26hdBW5OLNnIWGZ0BV6/OiBk742teLybLVNPCpYUcW405Ckg==", + "dependencies": { + "prosemirror-model": "^1.16.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/puppeteer": { + "version": "19.4.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-19.4.1.tgz", + "integrity": "sha512-PCnrR13B8A+VSEDXRmrNXRZbrkF1tfsI1hKSC7vs13eNS6CUD3Y4FA8SF8/VZy+Pm1kg5AggJT2Nu3HLAtGkFg==", + "hasInstallScript": true, + "dependencies": { + "cosmiconfig": "8.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "puppeteer-core": "19.4.1" + }, + "engines": { + "node": ">=14.1.0" + } + }, + "node_modules/puppeteer-core": { + "version": "19.4.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.4.1.tgz", + "integrity": "sha512-JHIuqtqrUAx4jGOTxXu4ilapV2jabxtVMA/e4wwFUMvtSsqK4nVBSI+Z1SKDoz7gRy/JUIc8WzmfocCa6SIZ1w==", + "dependencies": { + "cross-fetch": "3.1.5", + "debug": "4.3.4", + "devtools-protocol": "0.0.1068969", + "extract-zip": "2.0.1", + "https-proxy-agent": "5.0.1", + "proxy-from-env": "1.1.0", + "rimraf": "3.0.2", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "8.11.0" + }, + "engines": { + "node": ">=14.1.0" + } + }, + "node_modules/puppeteer/node_modules/cosmiconfig": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", + "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "peerDependencies": { + "react": "17.0.2" + } + }, + "node_modules/react-flow-renderer": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/react-flow-renderer/-/react-flow-renderer-10.3.12.tgz", + "integrity": "sha512-DTaz4HV0rA/qtvY80fjdb/QwIvtZEhqCQ2iAqfzFH08RjWCrLmESX4Nc400EB3CGcCK8/pDn/ta4cOS3udunTw==", + "dependencies": { + "@babel/runtime": "^7.18.9", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^3.7.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "16 || 17 || 18", + "react-dom": "16 || 17 || 18" + } + }, + "node_modules/react-flow-renderer/node_modules/zustand": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", + "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==", + "dev": true, + "dependencies": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha512-0dVfwYVOlf/LBA2ec4OwQ6p3X9mYxn/wOl2xTcLwjnPYrkgEfPx3VI4eGCH3rQLlPISG5v9I9bkZosKsNRTRKA==", + "dev": true, + "dependencies": { + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" + } + }, + "node_modules/reduce-css-calc/node_modules/balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha512-STw03mQKnGUYtoNjmowo4F2cRmIIxYEGiMsjjwla/u5P1lxadj/05WkNaFjNiKTgJkj8KiXbgAiRTmcQRwQNtg==", + "dev": true + }, + "node_modules/reduce-function-call": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.3.tgz", + "integrity": "sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "dev": true, + "dependencies": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "dev": true, + "dependencies": { + "rc": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" + }, + "node_modules/regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", + "dev": true, + "dependencies": { + "is-finite": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha512-H7AkJWMobeskkttHyhTVtS0fxpFLjxhbfMa6Bk3wimP7sdPRGL3EyCg3sAQenFfAe+xQ+oAc85Nmtvq0ROM83Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "dev": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/rifm": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/rifm/-/rifm-0.12.1.tgz", + "integrity": "sha512-OGA1Bitg/dSJtI/c4dh90svzaUPt228kzFsUkJbtA2c964IqEAwWXeL9ZJi86xWv3j5SMqRvGULl7bA6cK0Bvg==", + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==", + "dev": true, + "dependencies": { + "align-text": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rope-sequence": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.3.tgz", + "integrity": "sha512-85aZYCxweiD5J8yTEbw+E6A27zSnLPNDL0WfPdw3YYodq7WjnTKo0q4dtyQ2gz23iPT8Q9CUyJtAaUNcTxRf5Q==" + }, + "node_modules/run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", + "dev": true, + "dependencies": { + "aproba": "^1.1.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sass": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.0.tgz", + "integrity": "sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass-loader": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", + "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.0.1", + "neo-async": "^2.5.0", + "pify": "^4.0.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^3.0.0 || ^4.0.0" + } + }, + "node_modules/sass-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/sass-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dev": true, + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha512-gL8F8L4ORwsS0+iQ34yCYv///jsOq0ZL7WP55d1HnJ32o7tyFYEFQZQA22mrLIacZdU6xecaBBZ+uEiffGNyXw==", + "dev": true, + "dependencies": { + "semver": "^5.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", + "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", + "dev": true + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serviceworker-cache-polyfill": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz", + "integrity": "sha512-VMl1n99TbtKdO7DYNX0J9FQt1doo69V6fBniKC7o+CoJerbmFlQbsoxDa7P+b4b0tmpsdRIuzzS9sSJI7vFY2g==", + "dev": true + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "node_modules/sockjs-client": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.5.tgz", + "integrity": "sha512-PmPRkAYIeuRgX+ZSieViT4Z3Q23bLS2Itm/ck1tSf5P0/yVuFDiI5q9mcnpXoMdToaPSRS9MEyUx/aaBxrFzyw==", + "dev": true, + "dependencies": { + "debug": "^2.6.6", + "eventsource": "0.1.6", + "faye-websocket": "~0.11.0", + "inherits": "^2.0.1", + "json3": "^3.3.2", + "url-parse": "^1.1.8" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/sockjs-client/node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/sockjs-client/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dev": true, + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1" + }, + "bin": { + "strip-indent": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", + "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha512-jT/g9FFMoe9lu2IT6HtAxTA7RR2XOrmcrmCtGnyB/+GQnV6ZjNn+KOHZbZ35yL81+1F/aB6OeEsJztzBQ2EEwA==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dev": true, + "dependencies": { + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svgo/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/svgo/node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svgo/node_modules/js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha512-eIlkGty7HGmntbV6P/ZlAsoncFLGsNoM27lkTzS+oneY/EiNhj+geqD9ezg/ip+SW6Var0BJU2JtV0vEUZpWVQ==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/sw-precache": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/sw-precache/-/sw-precache-5.2.1.tgz", + "integrity": "sha512-8FAy+BP/FXE+ILfiVTt+GQJ6UEf4CVHD9OfhzH0JX+3zoy2uFk7Vn9EfXASOtVmmIVbL3jE/W8Z66VgPSZcMhw==", + "deprecated": "Please migrate to Workbox: https://developers.google.com/web/tools/workbox/guides/migrations/migrate-from-sw", + "dev": true, + "dependencies": { + "dom-urls": "^1.1.0", + "es6-promise": "^4.0.5", + "glob": "^7.1.1", + "lodash.defaults": "^4.2.0", + "lodash.template": "^4.4.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "pretty-bytes": "^4.0.2", + "sw-toolbox": "^3.4.0", + "update-notifier": "^2.3.0" + }, + "bin": { + "sw-precache": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/sw-precache-webpack-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sw-precache-webpack-plugin/-/sw-precache-webpack-plugin-1.0.0.tgz", + "integrity": "sha512-xXda8q9Nx1uCQ3m6TfPvw6X9OcTCnl6qB/afrUi+FF9CK7Lly29XHnvRrni0fzBU/1LKToHFDfi4jpLZuhL9JQ==", + "dev": true, + "dependencies": { + "del": "^3.0.0", + "lodash.template": "^4.5.0", + "sw-precache": "^5.2.1", + "uglify-es": "^3.3.9" + }, + "engines": { + "node": ">=4.0.0" + }, + "peerDependencies": { + "webpack": "^1 || ^2 || ^2.1.0-beta || ^2.2.0-beta || ^3 || ^4" + } + }, + "node_modules/sw-toolbox": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/sw-toolbox/-/sw-toolbox-3.6.0.tgz", + "integrity": "sha512-v/hu7KQQtospyDLpZxz7m5c7s90aj53YEkJ/A8x3mLPlSgIkZ6RKJkTjBG75P1p/fo5IeSA4TycyJg3VSu/aPw==", + "deprecated": "Please migrate to Workbox: https://developers.google.com/web/tools/workbox/guides/migrations/migrate-from-sw", + "dev": true, + "dependencies": { + "path-to-regexp": "^1.0.1", + "serviceworker-cache-polyfill": "^4.0.0" + } + }, + "node_modules/tapable": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", + "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha512-7dPUZQGy/+m3/wjVz3ZW5dobSoD/02NxJpoXUX0WIyjfVS3l0c+b/+9phIDFA7FHzkYtwtMFgeGZ/Y8jVTeqQQ==", + "dev": true, + "dependencies": { + "execa": "^0.7.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "peer": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/time-stamp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.2.0.tgz", + "integrity": "sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, + "node_modules/tiptap": { + "version": "1.32.2", + "resolved": "https://registry.npmjs.org/tiptap/-/tiptap-1.32.2.tgz", + "integrity": "sha512-5IwVj8nGo8y5V3jbdtoEd7xNUsi8Q0N6WV2Nfs70olqz3fldXkiImBrDhZJ4Anx8vhyP6PIBttrg0prFVmwIvw==", + "dependencies": { + "prosemirror-commands": "^1.1.4", + "prosemirror-dropcursor": "^1.3.2", + "prosemirror-gapcursor": "^1.1.5", + "prosemirror-inputrules": "^1.1.3", + "prosemirror-keymap": "^1.1.4", + "prosemirror-model": "^1.13.1", + "prosemirror-state": "^1.3.3", + "prosemirror-view": "^1.16.5", + "tiptap-commands": "^1.17.1", + "tiptap-utils": "^1.13.1" + }, + "peerDependencies": { + "vue": "^2.5.17", + "vue-template-compiler": "^2.5.17" + } + }, + "node_modules/tiptap-commands": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/tiptap-commands/-/tiptap-commands-1.17.1.tgz", + "integrity": "sha512-CyGvMD/c6fNer5LThWGtrVMXHAqHn93ivGQpqJ58x3HNZFuoIiF9QTWXAiWbY/4QrG0ANYHKCSe9n5afickTqw==", + "dependencies": { + "prosemirror-commands": "^1.1.4", + "prosemirror-inputrules": "^1.1.2", + "prosemirror-model": "^1.13.1", + "prosemirror-schema-list": "^1.1.4", + "prosemirror-state": "^1.3.3", + "prosemirror-tables": "^1.1.1", + "tiptap-utils": "^1.13.1" + } + }, + "node_modules/tiptap-extensions": { + "version": "1.35.2", + "resolved": "https://registry.npmjs.org/tiptap-extensions/-/tiptap-extensions-1.35.2.tgz", + "integrity": "sha512-TIMbHVJe0/3aVeTeCmqGbatDkfxduPYFOffNCmuKR+h6oQNzTu6rLVhRzoNqktfxIoi/b44SiDPorTjSN72dCw==", + "dependencies": { + "lowlight": "^1.17.0", + "prosemirror-collab": "^1.2.2", + "prosemirror-history": "^1.1.3", + "prosemirror-model": "^1.13.1", + "prosemirror-state": "^1.3.3", + "prosemirror-tables": "^1.1.1", + "prosemirror-transform": "^1.2.8", + "prosemirror-view": "^1.16.5", + "tiptap": "^1.32.2", + "tiptap-commands": "^1.17.1", + "tiptap-utils": "^1.13.1" + }, + "peerDependencies": { + "vue": "^2.5.17", + "vue-template-compiler": "^2.5.17" + } + }, + "node_modules/tiptap-utils": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/tiptap-utils/-/tiptap-utils-1.13.1.tgz", + "integrity": "sha512-RoCvMfkdu7fp9u7nsRr1OgsYU8RFjoHKHEKpx075rJ9X0t+j5Vxah9n6QzTTr4yjvcavq22WO2flFacm36zYtA==", + "dependencies": { + "prosemirror-model": "^1.13.1", + "prosemirror-state": "^1.3.3", + "prosemirror-tables": "^1.1.1" + } + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==", + "dev": true + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "node_modules/uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "deprecated": "support for ECMAScript is superseded by `uglify-js` as of v3.13.0", + "dev": true, + "dependencies": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-es/node_modules/commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "node_modules/uglify-es/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha512-qLq/4y2pjcU3vhlhseXGGJ7VbFO4pBANu0kwl8VCa9KEI0V8VfZIx2Fy3w01iSTA/pGwKZSmu/+I4etLNDdt5w==", + "dev": true, + "dependencies": { + "source-map": "~0.5.1", + "yargs": "~3.10.0" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + }, + "optionalDependencies": { + "uglify-to-browserify": "~1.0.0" + } + }, + "node_modules/uglify-js/node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-js/node_modules/cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==", + "dev": true, + "dependencies": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "node_modules/uglify-js/node_modules/yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A==", + "dev": true, + "dependencies": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + }, + "node_modules/uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==", + "dev": true, + "optional": true + }, + "node_modules/uglifyjs-webpack-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", + "dev": true, + "dependencies": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "engines": { + "node": ">= 4.8 < 5.0.0 || >= 5.10" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha512-46TFiBOzX7xq/PcSWfFwkyjpemdRnMe31UQF+os0y+1W3k95f6R4SEt02Hj4p3X0Mir9gfrkmOtshFidS0VPUg==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==", + "dev": true, + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/unbzip2-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "dev": true + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==", + "dev": true + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha512-N0XH6lqDtFH84JxptQoZYmloF4nzrQqqrAymNj+/gW60AO2AZgOcf4O/nUXJcYfyQkqvMo9lSupBZmmgvuVXlw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "dev": true, + "dependencies": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", + "dev": true + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==", + "dev": true, + "dependencies": { + "prepend-http": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "peer": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "node_modules/vue": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.8.tgz", + "integrity": "sha512-ncwlZx5qOcn754bCu5/tS/IWPhXHopfit79cx+uIlLMyt3vCMGcXai5yCG5y+I6cDmEj4ukRYyZail9FTQh7lQ==", + "dependencies": { + "@vue/compiler-sfc": "2.7.8", + "csstype": "^3.1.0" + } + }, + "node_modules/vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "node_modules/vue-loader": { + "version": "13.7.3", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-13.7.3.tgz", + "integrity": "sha512-ACCwbfeC6HjY2pnDii+Zer+MZ6sdOtwvLmDXRK/BoD3WNR551V22R6KEagwHoTRJ0ZlIhpCBkptpCU6+Ri/05w==", + "dev": true, + "dependencies": { + "consolidate": "^0.14.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "lru-cache": "^4.1.1", + "postcss": "^6.0.8", + "postcss-load-config": "^1.1.0", + "postcss-selector-parser": "^2.0.0", + "prettier": "^1.7.0", + "resolve": "^1.4.0", + "source-map": "^0.6.1", + "vue-hot-reload-api": "^2.2.0", + "vue-style-loader": "^3.0.0", + "vue-template-es2015-compiler": "^1.6.0" + }, + "peerDependencies": { + "css-loader": "*", + "vue-template-compiler": "^2.0.0" + } + }, + "node_modules/vue-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/vue-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vue-loader/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vue-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue-router": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.4.tgz", + "integrity": "sha512-x+/DLAJZv2mcQ7glH2oV9ze8uPwcI+H+GgTgTmb5I55bCgY3+vXWIsqbYUzbBSZnwFHEJku4eoaH/x98veyymQ==" + }, + "node_modules/vue-style-loader": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-3.1.2.tgz", + "integrity": "sha512-ICtVdK/p+qXWpdSs2alWtsXt9YnDoYjQe0w5616j9+/EhjoxZkbun34uWgsMFnC1MhrMMwaWiImz3K2jK1Yp2Q==", + "dev": true, + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-style-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/vue-style-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.8.tgz", + "integrity": "sha512-eQqdcUpJKJpBRPDdxCNsqUoT0edNvdt1jFjtVnVS/LPPmr0BU2jWzXlrf6BVMeODtdLewB3j8j3WjNiB+V+giw==", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "node_modules/vuera": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/vuera/-/vuera-0.2.7.tgz", + "integrity": "sha512-MZErNEK+xXjxBzIDFL9cdEtX3TDZk4pMJGB+WLrlFOVro9okS/FeOSpdPFkpdyZUImZN/or4CrmztA932ChGEQ==", + "peerDependencies": { + "react": ">= 15.2.0", + "react-dom": ">= 15.2.0", + "vue": ">= 2.2" + } + }, + "node_modules/vuetify": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.6.12.tgz", + "integrity": "sha512-qe3hcMpWmT1O15tp+p65lOS7UKZ/hQYQktCsw9iXx2u3RwVbX6GR82gY2iROrKsiAzYDvMgrYxWQwY/pUfkekw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/johnleider" + }, + "peerDependencies": { + "vue": "^2.6.4" + } + }, + "node_modules/vuex": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", + "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "peerDependencies": { + "vue": "^2.0.0" + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.5.tgz", + "integrity": "sha512-WJrK7i6w+ULuZsGscCezbCH4Aev5U3xY87vnSimzzEgPQhb0Sa0a1rE3c2jtEwrFtSfi61Jefw3jI5/DD/3jbQ==" + }, + "node_modules/watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "optionalDependencies": { + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.1" + } + }, + "node_modules/watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "dev": true, + "optional": true, + "dependencies": { + "chokidar": "^2.1.8" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "optional": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "optional": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/watchpack-chokidar2/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "optional": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "optional": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "optional": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "optional": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "optional": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/watchpack-chokidar2/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "optional": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/webpack": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", + "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", + "dev": true, + "dependencies": { + "acorn": "^5.0.0", + "acorn-dynamic-import": "^2.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "async": "^2.1.2", + "enhanced-resolve": "^3.4.0", + "escope": "^3.6.0", + "interpret": "^1.0.0", + "json-loader": "^0.5.4", + "json5": "^0.5.1", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "mkdirp": "~0.5.0", + "node-libs-browser": "^2.0.0", + "source-map": "^0.5.3", + "supports-color": "^4.2.1", + "tapable": "^0.2.7", + "uglifyjs-webpack-plugin": "^0.4.6", + "watchpack": "^1.4.0", + "webpack-sources": "^1.0.1", + "yargs": "^8.0.2" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", + "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", + "dev": true, + "dependencies": { + "memory-fs": "~0.4.1", + "mime": "^1.5.0", + "path-is-absolute": "^1.0.0", + "range-parser": "^1.0.3", + "time-stamp": "^2.0.0" + }, + "engines": { + "node": ">=0.6" + }, + "peerDependencies": { + "webpack": "^1.0.0 || ^2.0.0 || ^3.0.0" + } + }, + "node_modules/webpack-dev-server": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz", + "integrity": "sha512-7TdOKKt7G3sWEhPKV0zP+nD0c4V9YKUJ3wDdBwQsZNo58oZIRoVIu66pg7PYkBW8A74msP9C2kLwmxGHndz/pw==", + "dev": true, + "dependencies": { + "ansi-html": "0.0.7", + "array-includes": "^3.0.3", + "bonjour": "^3.5.0", + "chokidar": "^2.1.2", + "compression": "^1.7.3", + "connect-history-api-fallback": "^1.3.0", + "debug": "^3.1.0", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "^0.19.1", + "import-local": "^1.0.0", + "internal-ip": "1.2.0", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "selfsigned": "^1.9.1", + "serve-index": "^1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.1.5", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^5.1.0", + "webpack-dev-middleware": "1.12.2", + "yargs": "6.6.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">=4.7" + }, + "peerDependencies": { + "webpack": "^2.2.0 || ^3.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/webpack-dev-server/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/webpack-dev-server/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "dev": true, + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha512-6/QWTdisjnu5UHUzQGst+UOEuEVwIzFVGBjq3jMTFNs5WJQsH/X6nMURSaScIdF5txylr1Ao9bvbWiKi2yXbwA==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^4.2.0" + } + }, + "node_modules/webpack-dev-server/node_modules/yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha512-+QQWqC2xeL0N5/TE+TY6OGEqyNRM+g2/r712PDNYgiCdXYCApXf1vzfmDSLBxfGRwV+moTq/V8FnMI24JCm2Yg==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack/node_modules/has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/webpack/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack/node_modules/loader-utils/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/webpack/node_modules/supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha512-ycQR/UbvI9xIlEdQT1TQqwoXtEldExbCEAJgRo5YXlmSKjv6ThHnP9/vwGa1gr19Gfw+LkFd7KqYMhzrRC5JYw==", + "dev": true, + "dependencies": { + "has-flag": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha512-TNM20HMW67kxHRNCZdvLyiwE1ST6WyY5Ae+TG55V81NpvNwJ9+V4/po4LHA1R9afV/WrqzfedG2UJCk2+swirw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + }, + "peerDependencies": { + "webpack": "^1.9 || ^2 || ^2.1.0-beta || ^2.2.0-rc || ^3.0.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha512-mmIPAft2vTgEILgPeZFqE/wWh24SEsR/k+N9fJ3Jxrz44iDFy9aemCxdksfURSHYFCLmvs/d/7Iso5XjPpNfrA==", + "dev": true, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "node_modules/widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "dev": true, + "dependencies": { + "string-width": "^2.1.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "dependencies": { + "errno": "~0.1.7" + } + }, + "node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha512-1Dly4xqlulvPD3fZUQJLY+FUIeqN3N2MM3uqe4rCJftAvOjFa3jFGfctOgluGx4ahPbUCsZkmJILiP0Vi4T6lQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha512-3RiZrpLpjrzIAKgGdPktBcMP/eG5bDFlkI+PHle1qwzyVXyDQL+pD/eZaMoOOO0Y7LLBfjpucObuUm/icvbpKQ==", + "dev": true, + "dependencies": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + } + }, + "node_modules/yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha512-WhzC+xgstid9MbVUktco/bf+KJG+Uu6vMX0LN1sLJvwmbCQVxb4D8LzogobonKycNasCZLdOzTAk1SK7+K7swg==", + "dev": true, + "dependencies": { + "camelcase": "^4.1.0" + } + }, + "node_modules/yargs-parser/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargs/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", + "dev": true, + "dependencies": { + "pify": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargs/node_modules/read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", + "dev": true, + "dependencies": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zustand": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.0.0.tgz", + "integrity": "sha512-OrsfQTnRXF1LZ9/vR/IqN9ws5EXUhb149xmPjErZnUrkgxS/gAHGy2dPNIVkVvoxrVe1sIydn4JjF0dYHmGeeQ==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", + "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==" + }, + "@babel/core": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.9.tgz", + "integrity": "sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==", + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.9", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.9", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + } + }, + "@babel/eslint-parser": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.18.9.tgz", + "integrity": "sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ==", + "requires": { + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.9.tgz", + "integrity": "sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==", + "requires": { + "@babel/types": "^7.18.9", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "requires": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-builder-react-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.18.6.tgz", + "integrity": "sha512-2ndBVP5f9zwHWQeBr5EgqTAvFhPDViMW969bbJzRhKUUylnC39CdFZdVmqk+UtkxIpwm/efPgm3SzXUSlJnjAw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-builder-react-jsx-experimental": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.11.tgz", + "integrity": "sha512-4oGVOekPI8dh9JphkPXC68iIuP6qp/RPbaPmorRmEFbRAHZjSqxPjqHudn18GVDPgCuFM/KdFXc63C17Ygfa9w==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.10", + "@babel/helper-module-imports": "^7.12.5", + "@babel/types": "^7.12.11" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", + "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", + "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", + "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "requires": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "requires": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", + "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==" + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-replace-supers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", + "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==" + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" + }, + "@babel/helper-wrap-function": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.9.tgz", + "integrity": "sha512-cG2ru3TRAL6a60tfQflpEfs4ldiPwF6YW3zfJiRgmoFVIaC1vGnBBgatfec+ZUziPHkHSaXAuEck3Cdkf3eRpQ==", + "requires": { + "@babel/helper-function-name": "^7.18.9", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "requires": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.9.tgz", + "integrity": "sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz", + "integrity": "sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", + "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.18.8" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", + "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", + "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", + "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "requires": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", + "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", + "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", + "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "requires": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-identifier": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", + "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.11.tgz", + "integrity": "sha512-5nWOw6mTylaFU72BdZfa0dP1HsGdY3IMExpxn8LBE8dNmkQjB+W+sR+JwIdtbzkPvVuFviT3zyNbSUkuVTVxbw==", + "requires": { + "@babel/helper-builder-react-jsx": "^7.10.4", + "@babel/helper-builder-react-jsx-experimental": "^7.12.11", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.12.1" + } + }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz", + "integrity": "sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz", + "integrity": "sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz", + "integrity": "sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg==", + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.1", + "babel-plugin-polyfill-corejs3": "^0.5.2", + "babel-plugin-polyfill-regenerator": "^0.3.1", + "semver": "^6.3.0" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", + "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz", + "integrity": "sha512-XNRwQUXYMP7VLuy54cr/KS/WeL3AZeORhrmeZ7iewgu+X2eBqmpaLI/hzqr9ZxCeUoq0ASK4GUzSM0BDhZkLFw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/preset-env": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.9.tgz", + "integrity": "sha512-75pt/q95cMIHWssYtyfjVlvI+QEZQThQbKvR9xH+F/Agtw/s4Wfc2V9Bwd/P39VtixB7oWxGdH4GteTTwYJWMg==", + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.6", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.6", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.1", + "babel-plugin-polyfill-corejs3": "^0.5.2", + "babel-plugin-polyfill-regenerator": "^0.3.1", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.0.0.tgz", + "integrity": "sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-transform-react-display-name": "^7.0.0", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/plugin-transform-react-jsx-self": "^7.0.0", + "@babel/plugin-transform-react-jsx-source": "^7.0.0" + } + }, + "@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz", + "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.6", + "@babel/types": "^7.18.6" + } + }, + "@babel/traverse": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.9.tgz", + "integrity": "sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.9", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.9", + "@babel/types": "^7.18.9", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz", + "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==", + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + } + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "optional": true + }, + "@date-io/core": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@date-io/core/-/core-2.15.0.tgz", + "integrity": "sha512-3CRvQUEK7aF87NUOwcTtmJ2Rc1kN0D4jFQUfRoanuAnE4o5HzHx4E2YenjaKjSPWeZYiWG6ZhDomx5hp1AaCJA==" + }, + "@date-io/date-fns": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@date-io/date-fns/-/date-fns-2.15.0.tgz", + "integrity": "sha512-hkVeLm0jijHS2F9YVQcf0LSlD55w9xPvvIfuxDE0XWNXOTcRAAhqw2aqOxyeGbmHxc5U4HqyPZaqs9tfeTsomQ==", + "requires": { + "@date-io/core": "^2.15.0" + } + }, + "@date-io/dayjs": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@date-io/dayjs/-/dayjs-2.15.0.tgz", + "integrity": "sha512-wgYzwaXr9KxkHNYxrOb1t8fYLfAdjIf0Q86qdVCwANObcvyGcPBm0uFtpPK7ApeE4DJUlbuG0IX75TtO+uITwQ==", + "requires": { + "@date-io/core": "^2.15.0" + } + }, + "@date-io/luxon": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@date-io/luxon/-/luxon-2.15.0.tgz", + "integrity": "sha512-CxTRCo5AM96ainnYaTpe1NS9GiA78SIgXBScgeAresCS20AvMcOd5XKerDj+y/KLhbSQbU6WUDqG9QcsrImXyQ==", + "requires": { + "@date-io/core": "^2.15.0" + } + }, + "@date-io/moment": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@date-io/moment/-/moment-2.15.0.tgz", + "integrity": "sha512-AcYBjl3EnEGsByaM5ir644CKbhgJsgc1iWFa9EXfdb4fQexxOC8oCdPAurK2ZDTwg62odyyKa/05YE7ElYh5ag==", + "requires": { + "@date-io/core": "^2.15.0" + } + }, + "@emotion/babel-plugin": { + "version": "11.9.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.9.5.tgz", + "integrity": "sha512-n+9y6TSsvAsOc0hXmdVtqgU6B+ils+zrzTZGg1aV2BkZtBbL7DiasLSpu3fUFwXOkYm63j7+649yb+HhyZxYSA==", + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/plugin-syntax-jsx": "^7.17.12", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.5", + "@emotion/serialize": "^1.0.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.0.13" + } + }, + "@emotion/cache": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.9.3.tgz", + "integrity": "sha512-0dgkI/JKlCXa+lEXviaMtGBL0ynpx4osh7rjOXE71q9bIF8G+XhJgvi+wDu0B0IdCVx37BffiwXlN9I3UuzFvg==", + "requires": { + "@emotion/memoize": "^0.7.4", + "@emotion/sheet": "^1.1.1", + "@emotion/utils": "^1.0.0", + "@emotion/weak-memoize": "^0.2.5", + "stylis": "4.0.13" + } + }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "@emotion/is-prop-valid": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.3.tgz", + "integrity": "sha512-RFg04p6C+1uO19uG8N+vqanzKqiM9eeV1LDOG3bmkYmuOj7NbKNlFC/4EZq5gnwAIlcC/jOT24f8Td0iax2SXA==", + "requires": { + "@emotion/memoize": "^0.7.4" + } + }, + "@emotion/memoize": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", + "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" + }, + "@emotion/react": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.9.3.tgz", + "integrity": "sha512-g9Q1GcTOlzOEjqwuLF/Zd9LC+4FljjPjDfxSM7KmEakm+hsHXk+bYZ2q+/hTJzr0OUNkujo72pXLQvXj6H+GJQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@emotion/babel-plugin": "^11.7.1", + "@emotion/cache": "^11.9.3", + "@emotion/serialize": "^1.0.4", + "@emotion/utils": "^1.1.0", + "@emotion/weak-memoize": "^0.2.5", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.4.tgz", + "integrity": "sha512-1JHamSpH8PIfFwAMryO2bNka+y8+KA5yga5Ocf2d7ZEiJjb7xlLW7aknBGZqJLajuLOvJ+72vN+IBSwPlXD1Pg==", + "requires": { + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.4", + "@emotion/unitless": "^0.7.5", + "@emotion/utils": "^1.0.0", + "csstype": "^3.0.2" + } + }, + "@emotion/sheet": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.1.tgz", + "integrity": "sha512-J3YPccVRMiTZxYAY0IOq3kd+hUP8idY8Kz6B/Cyo+JuXq52Ek+zbPbSQUrVQp95aJ+lsAW7DPL1P2Z+U1jGkKA==" + }, + "@emotion/styled": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.9.3.tgz", + "integrity": "sha512-o3sBNwbtoVz9v7WB1/Y/AmXl69YHmei2mrVnK7JgyBJ//Rst5yqPZCecEJlMlJrFeWHp+ki/54uN265V2pEcXA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@emotion/babel-plugin": "^11.7.1", + "@emotion/is-prop-valid": "^1.1.3", + "@emotion/serialize": "^1.0.4", + "@emotion/utils": "^1.1.0" + } + }, + "@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "@emotion/utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz", + "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ==" + }, + "@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" + }, + "@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "peer": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "peer": true, + "requires": { + "type-fest": "^0.20.2" + } + } + } + }, + "@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/free-brands-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.4.tgz", + "integrity": "sha512-f1witbwycL9cTENJegcmcZRYyawAFbm8+c6IirLmwbbpqz46wyjbQYLuxOc7weXFXfB7QR8/Vd2u5R3q6JYD9g==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/free-regular-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz", + "integrity": "sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/react-fontawesome": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "requires": { + "prop-types": "^15.8.1" + } + }, + "@fortawesome/vue-fontawesome": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-2.0.8.tgz", + "integrity": "sha512-SRmP0q9Ox4zq8ydDR/hrH+23TVU1bdwYVnugLVaAIwklOHbf56gx6JUGlwES7zjuNYqzKgl8e39iYf6ph8qSQw==", + "requires": {} + }, + "@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "peer": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "peer": true + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@material-ui/core": { + "version": "4.12.4", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.4.tgz", + "integrity": "sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.11.5", + "@material-ui/system": "^4.12.2", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + }, + "dependencies": { + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "@material-ui/styles": { + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", + "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.5.1", + "jss-plugin-camel-case": "^10.5.1", + "jss-plugin-default-unit": "^10.5.1", + "jss-plugin-global": "^10.5.1", + "jss-plugin-nested": "^10.5.1", + "jss-plugin-props-sort": "^10.5.1", + "jss-plugin-rule-value-function": "^10.5.1", + "jss-plugin-vendor-prefixer": "^10.5.1", + "prop-types": "^15.7.2" + }, + "dependencies": { + "csstype": { + "version": "2.6.20", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", + "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" + } + } + }, + "@material-ui/system": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", + "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.3", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + }, + "dependencies": { + "csstype": { + "version": "2.6.20", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", + "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" + } + } + }, + "@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", + "requires": {} + }, + "@material-ui/utils": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", + "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", + "requires": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + }, + "dependencies": { + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "@mdi/font": { + "version": "5.9.55", + "resolved": "https://registry.npmjs.org/@mdi/font/-/font-5.9.55.tgz", + "integrity": "sha512-jswRF6q3eq8NWpWiqct6q+6Fg/I7nUhrxYJfiEM8JJpap0wVJLQdbKtyS65GdlK7S7Ytnx3TTi/bmw+tBhkGmg==" + }, + "@mui/base": { + "version": "5.0.0-alpha.91", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.91.tgz", + "integrity": "sha512-/W5amPDz+Lout4FtX5HOyx2Q+YL/EtZciFrx2DDRuUm4M/pWnjfDZAtM+0aqimEvuk3FU+/PuFc7IAyhCSX4Cg==", + "requires": { + "@babel/runtime": "^7.17.2", + "@emotion/is-prop-valid": "^1.1.3", + "@mui/types": "^7.1.5", + "@mui/utils": "^5.9.1", + "@popperjs/core": "^2.11.5", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + } + }, + "@mui/material": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.9.2.tgz", + "integrity": "sha512-FItBuj9bPdVier2g5OBG2HHlQLou4JuH3gdnY43tpJOrCpmWrbDVJZqrSufKJFO00qjvTYaGlJedIu+vXn79qw==", + "requires": { + "@babel/runtime": "^7.17.2", + "@mui/base": "5.0.0-alpha.91", + "@mui/system": "^5.9.2", + "@mui/types": "^7.1.5", + "@mui/utils": "^5.9.1", + "@types/react-transition-group": "^4.4.5", + "clsx": "^1.2.1", + "csstype": "^3.1.0", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.2" + } + }, + "@mui/private-theming": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.9.1.tgz", + "integrity": "sha512-eIh2IZJInNTdgPLMo9cruzm8UDX5amBBxxsSoNre7lRj3wcsu3TG5OKjIbzkf4VxHHEhdPeNNQyt92k7L78u2A==", + "requires": { + "@babel/runtime": "^7.17.2", + "@mui/utils": "^5.9.1", + "prop-types": "^15.8.1" + } + }, + "@mui/styled-engine": { + "version": "5.8.7", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.8.7.tgz", + "integrity": "sha512-tVqtowjbYmiRq+qcqXK731L9eWoL9H8xTRhuTgaDGKdch1zlt4I2UwInUe1w2N9N/u3/jHsFbLcl1Un3uOwpQg==", + "requires": { + "@babel/runtime": "^7.17.2", + "@emotion/cache": "^11.9.3", + "csstype": "^3.1.0", + "prop-types": "^15.8.1" + } + }, + "@mui/system": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.9.2.tgz", + "integrity": "sha512-iOvt9tVeFapHL7f7M6BSIiKGMx6RTRvAmc8ipMnQ/MR5Qsxwnyv7qKtNC/K11Rk13Xx0VPaPAhyvBcsr3KdpHA==", + "requires": { + "@babel/runtime": "^7.17.2", + "@mui/private-theming": "^5.9.1", + "@mui/styled-engine": "^5.8.7", + "@mui/types": "^7.1.5", + "@mui/utils": "^5.9.1", + "clsx": "^1.2.1", + "csstype": "^3.1.0", + "prop-types": "^15.8.1" + } + }, + "@mui/types": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.5.tgz", + "integrity": "sha512-HnRXrxgHJYJcT8ZDdDCQIlqk0s0skOKD7eWs9mJgBUu70hyW4iA6Kiv3yspJR474RFH8hysKR65VVSzUSzkuwA==", + "requires": {} + }, + "@mui/utils": { + "version": "5.10.3", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.10.3.tgz", + "integrity": "sha512-4jXMDPfx6bpMVuheLaOpKTjpzw39ogAZLeaLj5+RJec3E37/hAZMYjURfblLfTWMMoGoqkY03mNsZaEwNobBow==", + "requires": { + "@babel/runtime": "^7.18.9", + "@types/prop-types": "^15.7.5", + "@types/react-is": "^16.7.1 || ^17.0.0", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + } + }, + "@mui/x-date-pickers": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.0.tgz", + "integrity": "sha512-LcWJYFC/SdYbu0JSz6pgQcCh/cvllyoAqfpqf6iTex7jMSEk8i008pCntyrEVVcjGrx0fakDduSJthlnf0xNxQ==", + "requires": { + "@babel/runtime": "^7.18.9", + "@date-io/core": "^2.15.0", + "@date-io/date-fns": "^2.15.0", + "@date-io/dayjs": "^2.15.0", + "@date-io/luxon": "^2.15.0", + "@date-io/moment": "^2.15.0", + "@mui/utils": "^5.9.3", + "@types/react-transition-group": "^4.4.5", + "clsx": "^1.2.1", + "prop-types": "^15.7.2", + "react-transition-group": "^4.4.5", + "rifm": "^0.12.1" + } + }, + "@popperjs/core": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz", + "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==" + }, + "@puppeteer/replay": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@puppeteer/replay/-/replay-2.7.1.tgz", + "integrity": "sha512-w5lghKKto24XB7PGAyflkf1lSuFC+V4+UU5upJJkmWD+O0Q82H2PlHl/Wqwi9aQrngEkKSiK2xMK8Ycs5NddPA==", + "requires": { + "cli-table3": "0.6.3", + "colorette": "2.0.19", + "yargs": "17.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + } + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", + "optional": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "@types/react": { + "version": "17.0.48", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.48.tgz", + "integrity": "sha512-zJ6IYlJ8cYYxiJfUaZOQee4lh99mFihBoqkOSEGV+dFi9leROW6+PgstzQ+w3gWTnUfskALtQPGHK6dYmPj+2A==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-is": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", + "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", + "requires": { + "@types/react": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", + "requires": { + "@types/react": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "@vue/compiler-sfc": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.8.tgz", + "integrity": "sha512-2DK4YWKfgLnW9VDR9gnju1gcYRk3flKj8UNsms7fsRmFcg35slVTZEkqwBtX+wJBXaamFfn6NxSsZh3h12Ix/Q==", + "requires": { + "@babel/parser": "^7.18.4", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + }, + "dependencies": { + "postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + }, + "acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha512-GKp5tQ8h0KMPWIYGRHHXI1s5tUpZixZ3IHF2jAu42wSCf6In/G873s6/y4DdKdhWvzhu1T6mE1JgvnhAKqyYYQ==", + "dev": true, + "requires": { + "acorn": "^4.0.3" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug==", + "dev": true + } + } + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peer": true, + "requires": {} + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==", + "dev": true + }, + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==", + "dev": true, + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha512-JoAxEa1DfP9m2xfB/y2r/aKcwXNlltr4+0QSBC4TrLfcxyvepX2Pv0t/xpgGV5bGsDzCYV8SzjWgyCW0T9yYbA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-you-es5": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/are-you-es5/-/are-you-es5-2.1.2.tgz", + "integrity": "sha512-gkT2bLCfzyJJr3lYoxZKScdD/Yp5zzghi+3KawONTvH/7rrgU3RMXYvGQnOTlqEFVgZFpEGj/6bZ6sF/9YtddA==", + "requires": { + "acorn": "^6.0.6", + "array-flatten": "^2.1.0", + "commander": "^2.19.0", + "find-up": "^4.1.0" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "dev": true + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha512-WKExI/eSGgGAkWAO+wMVdFObZV7hQen54UpD1kCCTN3tvlL3W1jL4+lPP/M7MwoP7Q4RHzKtO3JQ4HxYEcd+xQ==", + "dev": true, + "requires": { + "browserslist": "^1.7.6", + "caniuse-db": "^1.0.30000634", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^5.2.16", + "postcss-value-parser": "^3.2.3" + }, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha512-qHJblDE2bXVRYzuDetv/wAeHOJyO97+9wxC1cdCtyzgNuSozOyRCiiLaCR1f71AN66lQdVVBipWm63V+a7bPOw==", + "dev": true, + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + } + } + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true + } + } + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "babel-eslint": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", + "integrity": "sha512-i2yKOhjgwUbUrJ8oJm6QqRzltIoFahGNPZ0HF22lUN4H1DW03JQyJm7WSv+I1LURQWjDNhVqFo04acYa07rhOQ==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "babel-traverse": "^6.23.1", + "babel-types": "^6.23.0", + "babylon": "^6.17.0" + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha512-Mke0DA0QjUWuJlhsE0ZPPhYiJkRap642SmI/4ztCFaUs6V2AiH1sfecc+57NgaryfAA2VR3v6O+CSjC1jZJKOA==", + "dev": true + } + } + }, + "babel-helper-bindify-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", + "integrity": "sha512-TYX2QQATKA6Wssp6j7jqlw4QLmABDN1olRdEHndYvBXdaXM5dcx6j5rN0+nd+aVL+Th40fAEYvvw/Xxd/LETuQ==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha512-gCtfYORSG1fUMX4kKraymq607FWgMWg+j42IFPc18kFQEsmtaibP4UrqsXt8FlEJle25HUd4tsoDR7H2wDhe9Q==", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha512-RL8n2NiEj+kKztlrVJM9JT1cXzzAdvWFh76xh/H1I4nKwunzE4INBXn8ieCZ+wh4zWszZk7NBS1s/8HR5jDkzQ==", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha512-bHkmjcC9lM1kmZcVpA5t2om2nzT/xiZpo6TJq7UlZ3wqKfzia4veeXbIhKvJXAMzhhEBd3cR1IElL5AenWEUpA==", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha512-qe5csbhbvq6ccry9G7tkXbzNtcDiH4r51rrPUbwwoTzZ18AqxWYRZT6AOmxrpxKnQBW0pYlBI/8vh73Z//78nQ==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-explode-class": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", + "integrity": "sha512-SFbWewr0/0U4AiRzsHqwsbOQeLXVa9T1ELdqEa2efcQB5KopTnunAqoj07TuHlN2lfTQNPGO/rJR4FMln5fVcA==", + "dev": true, + "requires": { + "babel-helper-bindify-decorators": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha512-Oo6+e2iX+o9eVvJ9Y5eKL5iryeRdsIkwRYheCuhYdVHsdEQysbc2z2QkqCLIYnNxkT5Ss3ggrHdXiDI7Dhrn4Q==", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha512-WfgKFX6swFB1jS2vo+DwivRN4NB8XUdM3ij0Y1gnC21y1tdBoe6xjVnd7NSI6alv+gZXCtJqvrTeMW3fR/c0ng==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha512-zAYl3tqerLItvG5cKYw7f1SpvIxS9zi7ohyGHaI9cgDUjAT6YcY9jIEH5CstetP5wHIVSceXwNS7Z5BpJg+rOw==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha512-Op9IhEaxhbRT8MDXx2iNuMgciu2V8lDvYCNQbDGjdBNCjaMvyLf4wl4A3b8IgndCyQF8TwfgsQ8T3VD8aX1/pA==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha512-VlPiWmqmGJp0x0oK27Out1D+71nVVCTSdlbhIVoaBAj2lUgrNjBCRR9+llO4lTSb2O4r7PJg+RobRkhBrf6ofg==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha512-RYqaPD0mQyQIFRu7Ho5wE2yvA/5jxqCIj/Lv4BXNq23mHYu/vxikOy2JueLiBxQknwapwrJeNCesvY0ZcfnlHg==", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha512-sLI+u7sXJh6+ToqDr57Bv973kCepItDhMou0xCP2YPVmR1jkHSCY+p1no8xErbV1Siz5QE8qKT1WIwybSWlqjw==", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha512-n7pFrqQm44TCYvrCDb0MqabAF+JUBq+ijBvNMUxpkLjJaAu32faIexewMumrH5KLLJ1HDyT0PTEqRyAe/GwwuQ==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha512-B1M5KBP29248dViEo1owyY32lk1ZSH2DaNNrXLGt8lyjjHm7pBqAdQ7VKUPR6EEDO323+OvT3MQXbCin8ooWdA==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "requires": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", + "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.2", + "semver": "^6.1.1" + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha512-4Zp4unmHgw30A1eWI5EpACji2qMocisdXhAftfhXoSV9j0Tvj6nRFE3tOmRY912E0FMRm/L5xWE7MGVT2FoLnw==", + "dev": true + }, + "babel-plugin-syntax-async-generators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "integrity": "sha512-EbciFN5Jb9iqU9bqaLmmFLx2G8pAUsvpWJ6OzOWBNrSY9qTohXj+7YfZx6Ug1Qqh7tCb1EA7Jvn9bMC1HBiucg==", + "dev": true + }, + "babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha512-chI3Rt9T1AbrQD1s+vxw3KcwC9yHtF621/MacuItITfZX344uhQoANjpoSJZleAmW2tjlolqB/f+h7jIqXa7pA==", + "dev": true + }, + "babel-plugin-syntax-decorators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "integrity": "sha512-AWj19x2aDm8qFQ5O2JcD6pwJDW1YdcnO+1b81t7gxrGjz5VHiUqeYWAR4h7zueWMalRelrQDXprv2FrY1dbpbw==", + "dev": true + }, + "babel-plugin-syntax-dynamic-import": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "integrity": "sha512-MioUE+LfjCEz65Wf7Z/Rm4XCP5k2c+TbMd2Z2JKc7U9uwjBhAfNPE48KC4GTGKhppMeYVepwDBNO/nGY6NYHBA==", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha512-Z/flU+T9ta0aIEKl1tGEmN/pZiI1uXmCiGFRegKacQfEJzp7iNsKloZmyJlQr+75FCJtiFfGIK03SiCvCt9cPQ==", + "dev": true + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha512-C4Aq+GaAj83pRQ0EFgTvw5YO6T3Qz2KGrNRwIj9mSoNHVvdZY4KO2uA6HNtNXCw993iSZnckY1aLW8nOi8i4+w==" + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha512-Gx9CH3Q/3GKbhs07Bszw5fPTlU+ygrOGfAhEt7W2JICwufpC4SuO0mG0+4NykPBSYPMJhqvVlDBU17qB1D+hMQ==", + "dev": true + }, + "babel-plugin-transform-async-generator-functions": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", + "integrity": "sha512-uT7eovUxtXe8Q2ufcjRuJIOL0hg6VAUJhiWJBLxH/evYAw+aqoJLcYTR8hqx13iOx/FfbCMHgBmXWZjukbkyPg==", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-generators": "^6.5.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha512-7BgYJujNCg0Ti3x0c/DL3tStvnKS6ktIYOmo9wginv/dfZOrbSZ+qG4IRRHMBOzZ5Awb1skTiAsQXg/+IWkZYw==", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha512-n4jtBA3OYBdvG5PRMKsMXJXHfLYw/ZOmtxCLOOwz6Ro5XlrColkStLnz1AS1L2yfPA9BKJ1ZNlmVCLjAL9DSIg==", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", + "integrity": "sha512-skQ2CImwDkCHu0mkWvCOlBCpBIHW4/49IZWVwV4A/EnWjL9bB6UBvLyMNe3Td5XDStSZNhe69j4bfEW8dvUbew==", + "dev": true, + "requires": { + "babel-helper-explode-class": "^6.24.1", + "babel-plugin-syntax-decorators": "^6.13.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha512-PCqwwzODXW7JMrzu+yZIaYbPQSKjDTAsNNlK2l5Gg9g4rz2VzLnZsStvp/3c46GfXpwkyufb3NCyG9+50FF1Vg==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha512-2+ujAT2UMBzYFm7tidUsYh+ZoIutxJ3pN9IYrF1/H6dCKtECfhmB8UkHVpyxDwkj0CYbQG35ykoz925TUnBc3A==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha512-YiN6sFAQ5lML8JjCmr7uerS5Yc/EMbgg9G8ZNmk2E3nYX4ckHR01wrkeeMijEf5WHNK5TW0Sl0Uu3pv3EdOJWw==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha512-5Dy7ZbRinGrNtmWpquZKZ3EGY8sDgIVB4CU8Om8q8tnMLrD/m94cKglVcHps0BCTdZ0TJeeAWOq2TK9MIY6cag==", + "dev": true, + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha512-C/uAv4ktFP/Hmh01gMTvYvICrKze0XVX9f2PdIXuriCSvUmV9j+u+BB9f5fJK3+878yMK6dkdcq+Ymr9mrcLzw==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha512-aNv/GDAW0j/f4Uy1OEPZn1mqD+Nfy9viFGBfQ5bZyT35YqOiqx7/tXdyfZkJ1sC21NyEsBdfDY6PYmLHF4r5iA==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha512-ossocTuPOssfxO2h+Z3/Ea1Vo1wWx31Uqy9vIiJusOP4TbF7tPs9U0sJ9pX9OJPf4lXRGj5+6Gkl/HHKiAP5ug==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha512-DLuRwoygCoXx+YfxHLkVx5/NpeSbVwfoTeBykpJK7JhYWlL/O8hgAK/reforUnZDlxasOrVPPJVI/guE3dCwkw==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha512-iFp5KIcorf11iBqu/y/a7DK3MN5di3pNCzto61FqCNnUX4qeBwcV1SLqe10oXNnCaxBUImX3SckX2/o1nsrTcg==", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha512-tjFl0cwMPpDYyoqYA9li1/7mGFit39XiNX5DKC/uCNjBctMxyL1/PT/l4rSlbvBG1pOKI88STRdUsWXB3/Q9hQ==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha512-LnIIdGWIKdw7zwckqx+eGjcS8/cl8D74A3BpJbGjKTFFNJSMrjN4bIh22HY1AlkUbeLG6X6OZj56BDvWD+OeFA==", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha512-ONFIPsq8y4bls5PPsAWYXH/21Hqv64TBxdje0FvU3MhIV6QM2j5YS7KvAzg/nTIVLot2D2fmFQrFWCbgHlFEjg==", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha512-LpVbiT9CLsuAIp3IG0tfbVo81QIhn6pE8xBJ7XSeCtFlMltuar5VuBV6y6Q45tpui9QWcy5i0vLQfCfrnF7Kiw==", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha512-8G5hpZMecb53vpD3mjs64NhI1au24TAmokQ4B+TBFBjN9cVoGoOvotdrMMRmHvVZUEvqGUPWL514woru1ChZMA==", + "dev": true, + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha512-8HxlW+BB5HqniD+nLkQ4xSAVq3bR/pcYW9IigY+2y0dI+Y7INFeTbfAQr+63T3E4UDsZGjyb+l9txUnABWxlOQ==", + "dev": true, + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha512-mDdocSfUVm1/7Jw/FIRNw9vPrBQNePy6wZJlR8HAUBLybNp1w/6lr6zZ2pjMShee65t/ybR5pT8ulkLzD1xwiw==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha512-3Ghhi26r4l3d0Js933E5+IhHwk0A1yiutj9gwvzmFbVV0sPMYk2lekhOufHBswX7NCoSeF4Xrl3sCIuSIa+zOg==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha512-CYP359ADryTo3pCsH0oxRo/0yn6UsEZLqYohHmvLQdfS9xkf+MbCzE3/Kolw9OYIY4ZMilH25z/5CbQbwDD+lQ==", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha512-x8b9W0ngnKzDMHimVtTfn5ryimars1ByTqsfBDwAqLibmuuQY6pgBQi5z1ErIsUOWBdw1bW9FSz5RZUojM4apg==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha512-fz6J2Sf4gYN6gWgRZaoFXmq93X+Li/8vf+fb0sGDVtdeWvxC9y5/bTD7bvfWMEq6zetGEHpWjtzRGSugt5kNqw==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha512-v61Dbbihf5XxnYjtBN04B/JBvsScY37R1cZT5r9permN1cp+b70DY3Ib3fIkgn1DI9U3tGgBJZVD8p/mE/4JbQ==", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha512-tJ9+S4oKjxY8IZ9jmjnp/mtytu1u3iyIQAfmI51IKWH6bFf7XR1ybtaO6j7INhZKXOTYADk7V5qxaqLkmNxiZQ==", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha512-x+Y3yA24uF68m5GA+tBjbGYo64xXVJpbToBaWCoSNSc1hdk6dfctaRWrNFTVJZIIhL5GxW8zwjoixbnifnK59g==", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha512-jlQ9gYLfk2p3V5Ag5fYhA7fv7OHzd1KUH0PRP46xc3TgwjwgROIW572AfYg/X9kaNq/LJnu6oJcFRXlIrGoTRw==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha512-LzXDmbMkklvNhprr20//RStKVcT8Cu+SQtX18eMHLhjHf2yFzwtQ0S2f0jQ+89rokoNdmwoSqYzAhq86FxlLSQ==", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha512-ocgA9VJvyxwt+qJB0ncxV8kb/CjfTcECUY4tQ5VT7nP6Aohzobm8CDFaQ5FHdvZQzLmf0sgDxB8iRXZXxwZcyA==", + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha512-LS+dBkUGlNR15/5WHKe/8Neawx663qttS6AGqoOUhICc9d1KciBvtrQSuc0PI+CxQ2Q/S1aKuJ+u64GtLdcEZg==", + "dev": true, + "requires": { + "regenerator-transform": "^0.10.0" + }, + "dependencies": { + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + } + } + }, + "babel-plugin-transform-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", + "integrity": "sha512-cpGMVC1vt/772y3jx1gwSaTitQVZuFDlllgreMsZ+rTYC6jlYXRyf5FQOgSnckOiA5QmzbXTyBY2A5AmZXF1fA==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha512-j3KtSpjyLSJxNoCDrhwiJad8kw0gJ9REGj8/CqL0HeRyLnvUNYV9zcqluL6QJSXh3nfsLEmSLvwRfGzrgR96Pw==", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha512-F2rZGQnAdaHWQ8YAoeRbukc7HS9QgdgeyJ0rQDd485v9opwuPvjpPFcOOT/WmkKTdgy9ESgSPXDcTNpzrGr6iQ==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==", + "dev": true + } + } + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha512-XfwUqG1Ry6R43m4Wfob+vHbIVBIqTg/TJY4Snku1iIzeH7mUnwHA8Vagmv+ZQbPwhS8HgsdQvy28Py3k5zpoFQ==", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, + "babel-preset-stage-2": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", + "integrity": "sha512-9F+nquz+37PrlTSBdpeQBKnQfAMNBnryXw+m4qBh35FNbJPfzZz+sjN2G5Uf1CRedU9PH7fJkTbYijxmkLX8Og==", + "dev": true, + "requires": { + "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-decorators": "^6.24.1", + "babel-preset-stage-3": "^6.24.1" + } + }, + "babel-preset-stage-3": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", + "integrity": "sha512-eCbEOF8uN0KypFXJmZXn2sTk7bPV9uM5xov7G/7BM08TbQEObsVs0cEWfy6NQySlfk7JBi/t+XJP1JkruYfthA==", + "dev": true, + "requires": { + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-generator-functions": "^6.24.1", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-exponentiation-operator": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.22.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha512-veliHlHX06wjaeY8xNITbveXSiI+ASFnOqvne/LaIJIqOWi2Ogmj91KOugEz/hoh/fwMhXNBJPCv8Xaz5CyM4A==", + "dev": true, + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + }, + "dependencies": { + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==", + "dev": true + } + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "requires": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + } + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha512-SBTl70K0PkDUIebbkXrxWqZlHNs0wRgRD6QZ8guctShjbh63gEPfF+Wj0Yw+75f5Y8tSzqAI/NcisYv/cCah2Q==", + "dev": true, + "requires": { + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + }, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha512-qHJblDE2bXVRYzuDetv/wAeHOJyO97+9wxC1cdCtyzgNuSozOyRCiiLaCR1f71AN66lQdVVBipWm63V+a7bPOw==", + "dev": true, + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + } + } + }, + "caniuse-db": { + "version": "1.0.30001373", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001373.tgz", + "integrity": "sha512-NOoFLQ0w7geqot8ENHEE/cRqQN0HdVtJeG2h+2cjmEYb07X0HGwBQxREKWpt5YUhNPmAxHKVGPbak1FLey6GGw==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001372", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001372.tgz", + "integrity": "sha512-tBgBhIXBIqORB9ieUEYBKRfSlaF6YPq7WNNqcreF6Cl24UKNGIvE5/rP59dOGN6TRIS/5zOMHAMSRrVNmifWWw==" + }, + "capture-stack-trace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", + "dev": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==", + "dev": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + } + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "clap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", + "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", + "dev": true, + "requires": { + "chalk": "^1.1.3" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true + } + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "classcat": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.3.tgz", + "integrity": "sha512-6dK2ke4VEJZOFx2ZfdDAl5OhEL8lvkl6EHF92IfRePfHxQTqir5NlcNVUv+2idjDqCX2NDc8m8YSAI5NI975ZQ==" + }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==", + "dev": true + }, + "cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "requires": { + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + }, + "coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha512-KAGck/eNAmCL0dcT3BiuYwLbExK6lduR8DxM3C1TyDzaXhZHyZ8ooX5I5+na2e3dPFuibfxrGdorr0/Lr7RYCQ==", + "dev": true, + "requires": { + "q": "^1.1.2" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha512-Ajpjd8asqZ6EdxQeqGzU5WBhhTfJ/0cA4Wlbre7e5vXfmDSmda7Ov6jeKoru+b0vHcb1CqvuroTHp5zIWzhVMA==", + "dev": true, + "requires": { + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha512-sz29j1bmSDfoAxKIEU6zwoIZXN6BrFbAMIhfYCNyiZXBDuU/aiHlN84lp/xDzL2ubyFhLDobHIlU1X70XRrMDA==", + "dev": true, + "requires": { + "color-name": "^1.0.0" + } + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "colormin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha512-XSEQUUQUR/lXqGyddiNH3XYFUPYlYr1vXy9rTFMsSOw+J7Q6EQkdlQIrTlYn4TccpsOaUE1PYQNjBn20gwCdgQ==", + "dev": true, + "requires": { + "color": "^0.11.0", + "css-color-names": "0.0.4", + "has": "^1.0.1" + } + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "configstore": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", + "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", + "dev": true, + "requires": { + "dot-prop": "^4.2.1", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + }, + "dependencies": { + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "consolidate": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.14.5.tgz", + "integrity": "sha512-PZFskfj64QnpKVK9cPdY36pyWEhZNM+srRVqtwMiVTlnViSoZcvX35PpBhhUcyLTHXYvz7pZRmxvsqwzJqg9kA==", + "dev": true, + "requires": { + "bluebird": "^3.1.1" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "dev": true + }, + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + }, + "core-js-compat": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.0.tgz", + "integrity": "sha512-F+2E63X3ff/nj8uIrf8Rf24UDGIz7p838+xjEp+Bx3y8OWXj+VTPPZNCtdqovPaS9o7Tka5mCH01Zn5vOd6UQg==", + "requires": { + "browserslist": "^4.21.2", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", + "dev": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-env": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", + "integrity": "sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.5" + } + }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "requires": { + "node-fetch": "2.6.7" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", + "dev": true + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==", + "dev": true + }, + "css-loader": { + "version": "0.28.11", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", + "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "css-selector-tokenizer": "^0.7.0", + "cssnano": "^3.10.0", + "icss-utils": "^2.1.0", + "loader-utils": "^1.0.2", + "lodash.camelcase": "^4.3.0", + "object-assign": "^4.1.1", + "postcss": "^5.0.6", + "postcss-modules-extract-imports": "^1.2.0", + "postcss-modules-local-by-default": "^1.2.0", + "postcss-modules-scope": "^1.1.0", + "postcss-modules-values": "^1.3.0", + "postcss-value-parser": "^3.3.0", + "source-list-map": "^2.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, + "css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "requires": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha512-0o0IMQE0Ezo4b41Yrm8U6Rp9/Ag81vNXY1gZMnT1XhO4DpjEf2utKERqWJbOoz3g1Wdc1d3QSta/cIuJ1wSTEg==", + "dev": true, + "requires": { + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" + } + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha512-FmCI/hmqDeHHLaIQckMhMZneS84yzUZdrWDAvJVVxOwcKE1P1LF9FGmzr1ktIQSxOw6fl3PaQsmfg+GN+VvR3w==", + "dev": true, + "requires": { + "clap": "^1.0.9", + "source-map": "^0.5.3" + } + }, + "csstype": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", + "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==", + "dev": true + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" + }, + "d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" + }, + "d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + } + }, + "d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" + }, + "d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "requires": { + "d3-color": "1 - 3" + } + }, + "d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" + }, + "d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" + }, + "d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "requires": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + } + }, + "d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + } + }, + "dayjs": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", + "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==" + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "peer": true + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==", + "dev": true + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha512-7yjqSoVSlJzA4t/VUwazuEagGeANEKB3f/aNI//06pfKgwoCb7f6Q1gETN1sZzYaj6chTQ0AhIwDiPdfOjko4A==", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "devtools-protocol": { + "version": "0.0.1068969", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1068969.tgz", + "integrity": "sha512-ATFTrPbY1dKYhPPvpjtwWKSK2mIwGmRwX54UASn9THEuIZCe2n9k3vVuMmt6jWeL+e5QaaguEv/pMyR+JQB7VQ==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "peer": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "dom-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/dom-urls/-/dom-urls-1.1.0.tgz", + "integrity": "sha512-LNxCeExaNbczqMVfQUyLdd+r+smG7ixIa+doeyiJ7nTmL8aZRrJhHkEYBEYVGvYv7k2DOEBh2eKthoCmWpfICg==", + "dev": true, + "requires": { + "urijs": "^1.16.1" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "dot-prop": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "duplexer3": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.204", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.204.tgz", + "integrity": "sha512-5Ojjtw9/c9HCXtMVE6SXVSHSNjmbFOXpKprl6mY/5moLSxLeWatuYA7KTD+RzJMxLRH6yNNQrqGz9p6IoNBMgw==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", + "integrity": "sha512-ZaAux1rigq1e2nQrztHn4h2ugvpzZxs64qneNah+8Mh/K0CRqJFJc+UoXnUsq+1yX+DmQFPPdVqboKAJ89e0Iw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.7" + } + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.61", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", + "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha512-7S8YXIcUfPMOr3rqJBVMePAbRsD1nWeSMQ86K/lDI76S3WKXz+KWILvTIPbTroubOkZTGh+b+7/xIIphZXNYbA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + }, + "dependencies": { + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha512-exfuQY8UGtn/N+gL1iKkH8fpNd5sJ760nJq6mmZAHldfxMD5kX07lbQuYlspoXsuknXNv9Fb7y2GsPOnQIbxHg==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha512-75IUQsusDdalQEW/G/2esa87J7raqdJF+Ca0/Xm5C3Q58Nr4yVYjZGp/P1+2xiEVgXRrA39dpRb8LcshajbqDQ==", + "dev": true, + "requires": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.20.0.tgz", + "integrity": "sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA==", + "peer": true, + "requires": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "peer": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "peer": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "peer": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "peer": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "peer": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "peer": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "peer": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "peer": true + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "peer": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "peer": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "peer": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "peer": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "peer": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "peer": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "peer": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" + }, + "espree": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "peer": true, + "requires": { + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "dependencies": { + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "peer": true + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "peer": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "peer": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "peer": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "eventsource": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "integrity": "sha512-bbB5tEuvC+SuRUG64X8ghvjgiRniuA4WlehWbFnoN4z6TxDXpyX+BMHF7rMgZAqoe+EbyNRUbHN0uuP9phy5jQ==", + "dev": true, + "requires": { + "original": ">=0.0.5" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "dev": true, + "requires": { + "type": "^2.5.0" + }, + "dependencies": { + "type": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", + "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==", + "dev": true + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + } + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "peer": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "requires": { + "format": "^0.2.0" + } + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "requires": { + "pend": "~1.2.0" + } + }, + "fiber": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fiber/-/fiber-1.0.4.tgz", + "integrity": "sha512-TASLDBU4gPNnv1oSMfvJtOJ0yN1k3s8KMzE7jrbCZo6xIEapMURT+Cay2xG5WBWfhay64D99G//dIlXj2PGXiA==" + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "peer": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "peer": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "peer": true + }, + "flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true + }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==" + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "peer": true + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "dev": true + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "peer": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + } + } + }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", + "dev": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + } + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "highcharts": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-9.3.3.tgz", + "integrity": "sha512-QeOvm6cifeZYYdTLm4IxZsXcOE9c4xqfs0z0OJJ0z7hhA9WG0rmcVAyuIp5HBl/znjA/ayYHmpYjBYD/9PG4Fg==" + }, + "highcharts-vue": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/highcharts-vue/-/highcharts-vue-1.4.0.tgz", + "integrity": "sha512-fTPqJmU/wR/nUEtofbC/iJqcgULGcP3C0Hd4YYCKH2FiiteNs7UidjSM4eYj5Y4ldpOVhwPtNj/m6XwC3ajp6g==", + "requires": {} + }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha512-ycURW7oUxE2sNiPVw1HVEFsW+ecOpJ5zaj7eC0RlwhibhRBod20muUN8qu/gzx956YrLolVvs1MTXwKgC2rVEg==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "dev": true + }, + "html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz", + "integrity": "sha512-aYk1rTKqLTus23X3L96LGNCGNgWpG4cG0XoZIT1GUPhhulEHX/QalnO6Vbo+WmKWi4AL2IidjuC0wZtbpg0yhQ==", + "dev": true, + "requires": { + "http-proxy": "^1.18.1", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==", + "dev": true + }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha512-bsVoyn/1V4R1kYYjLcWLedozAM4FClZUdjE9nIr8uWY7xs78y9DATgwz2wGU7M+7z55KenmmTkN2DVJ7bqzjAA==", + "dev": true, + "requires": { + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "peer": true + }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", + "dev": true + }, + "import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "dev": true, + "requires": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "internal-ip": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", + "integrity": "sha512-DzGfTasXPmwizQP4XV2rR6r2vp8TjlOpMnJqG9Iy2i1pl1lkZdZj5rSpIc7YFGX2nS46PPgAGEyT+Q5hE2FB2g==", + "dev": true, + "requires": { + "meow": "^3.3.0" + } + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "dev": true + }, + "ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "dev": true + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" + }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==", + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "dev": true + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha512-Ya1giYJUkcL/94quj0+XGcmts6cETPBW1MiFz1ReJrnDJ680F52qpAEGAEGU0nq96FRGIGPx6Yo1CyPXcOoyGw==", + "dev": true, + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "peer": true + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + }, + "jss": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.9.2.tgz", + "integrity": "sha512-b8G6rWpYLR4teTUbGd4I4EsnWjg7MN0Q5bSsjKhVkJVjhQDy2KzkbD2AW3TuT0RYZVmZZHKIrXDn6kjU14qkUg==", + "requires": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-camel-case": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.2.tgz", + "integrity": "sha512-wgBPlL3WS0WDJ1lPJcgjux/SHnDuu7opmgQKSraKs4z8dCCyYMx9IDPFKBXQ8Q5dVYij1FFV0WdxyhuOOAXuTg==", + "requires": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.9.2" + } + }, + "jss-plugin-default-unit": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.2.tgz", + "integrity": "sha512-pYg0QX3bBEFtTnmeSI3l7ad1vtHU42YEEpgW7pmIh+9pkWNWb5dwS/4onSfAaI0kq+dOZHzz4dWe+8vWnanoSg==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.9.2" + } + }, + "jss-plugin-global": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.2.tgz", + "integrity": "sha512-GcX0aE8Ef6AtlasVrafg1DItlL/tWHoC4cGir4r3gegbWwF5ZOBYhx04gurPvWHC8F873aEGqge7C17xpwmp2g==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.9.2" + } + }, + "jss-plugin-nested": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.2.tgz", + "integrity": "sha512-VgiOWIC6bvgDaAL97XCxGD0BxOKM0K0zeB/ECyNaVF6FqvdGB9KBBWRdy2STYAss4VVA7i5TbxFZN+WSX1kfQA==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.9.2", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.2.tgz", + "integrity": "sha512-AP1AyUTbi2szylgr+O0OB7gkIxEGzySLITZ2GpsaoX72YMCGI2jYAc+WUhPfvUnZYiauF4zTnN4V4TGuvFjJlw==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.9.2" + } + }, + "jss-plugin-rule-value-function": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.2.tgz", + "integrity": "sha512-vf5ms8zvLFMub6swbNxvzsurHfUZ5Shy5aJB2gIpY6WNA3uLinEcxYyraQXItRHi5ivXGqYciFDRM2ZoVoRZ4Q==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.9.2", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-vendor-prefixer": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.2.tgz", + "integrity": "sha512-SxcEoH+Rttf9fEv6KkiPzLdXRmI6waOTcMkbbEFgdZLDYNIP9UKNHFy6thhbRKqv0XMQZdrEsbDyV464zE/dUA==", + "requires": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.9.2" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==", + "dev": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "peer": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", + "dev": true + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "peer": true + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "requires": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "math-expression-evaluator": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.4.0.tgz", + "integrity": "sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha512-nOBDrc/wgpkd3X/JOhMqYR+/eLqlfLP4oQfoBA6QExIxEl+GU01oyEkwWyueyO8110pUKijtiHGhEmYoOn88oQ==", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", + "dev": true + }, + "nan": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", + "dev": true, + "optional": true + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "peer": true + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + } + } + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "peer": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "orderedmap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.0.0.tgz", + "integrity": "sha512-buf4PoAMlh45b8a8gsGy/X6w279TSqkyAS0C0wdTSJwFSU+ljQFJON5I8NfjLHoCXwpSROIo2wr0g33T+kQshQ==" + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==", + "dev": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true + } + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha512-iBcptYFq+QUh9gzP7ta2btw50o40s4uLI4UDVgd5yRAZtUDWc5APdl5yQDd2h/TyiZNbJrv0HiYhT102CMgN7Q==", + "dev": true, + "requires": { + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" + } + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha512-XXitQe+jNNPf+vxvQXIQ1+pvdQKWKgkx8zlJNltcMEmLma1ypDRDQwlLt+6cP26fBreihNhZxohh1rcgCH2W5w==", + "dev": true, + "requires": { + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha512-SE7mf25D3ORUEXpu3WUqQqy0nCbMuM5BEny+ULE/FXdS/0UMA58OdzwvzuHJRpIFlk1uojt16JhaEogtP6W2oA==", + "dev": true, + "requires": { + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" + } + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha512-yGbyBDo5FxsImE90LD8C87vgnNlweQkODMkUZlDVM/CBgLr9C5RasLGJxxh9GjVOBeG8NcCMatoqI1pXg8JNXg==", + "dev": true, + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha512-+lk5W1uqO8qIUTET+UETgj9GWykLC3LOldr7EehmymV0Wu36kyoHimC4cILrAAYpHQ+fr4ypKcWcVNaGzm0reA==", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha512-IBFoyrwk52dhF+5z/ZAbzq5Jy7Wq0aLUsOn69JNS+7YeuyHaNzJwBIYE0QlUH/p5d3L+OON72Fsexyb7OK/3og==", + "dev": true, + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha512-IyKoDL8QNObOiUc6eBw8kMxBHCfxUaERYTUe2QF8k7j/xiirayDzzkmlR6lMQjrAM1p1DDRTvWrS7Aa8lp6/uA==", + "dev": true, + "requires": { + "postcss": "^5.0.16" + } + }, + "postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha512-nCbFNfqYAbKCw9J6PSJubpN9asnrwVLkRDFc4KCwyUEdOtM5XDE/eTW3OpqHrYY1L4fZxgan7LLRAAYYBzwzrg==", + "dev": true, + "requires": { + "postcss": "^5.0.14", + "uniqs": "^2.0.0" + } + }, + "postcss-filter-plugins": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", + "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-load-config": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", + "integrity": "sha512-3fpCfnXo9Qd/O/q/XL4cJUhRsqjVD2V1Vhy3wOEcLE5kz0TGtdDXJSoiTdH4e847KphbEac4+EZSH4qLRYIgLw==", + "dev": true, + "requires": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0", + "postcss-load-options": "^1.2.0", + "postcss-load-plugins": "^2.3.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "dev": true, + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + } + } + }, + "postcss-load-options": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", + "integrity": "sha512-WKS5LJMZLWGwtfhs5ahb2ycpoYF3m0kK4QEaM+elr5EpiMt0H296P/9ETa13WXzjPwB0DDTBiUBBWSHoApQIJg==", + "dev": true, + "requires": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "dev": true, + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + } + } + }, + "postcss-load-plugins": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", + "integrity": "sha512-/WGUMYhKiryWjYO6c7kAcqMuD7DVkaQ8HcbQenDme/d3OBOmrYMFObOKgUWyUy1uih5U2Dakq8H6VcJi5C9wHQ==", + "dev": true, + "requires": { + "cosmiconfig": "^2.1.1", + "object-assign": "^4.1.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "dev": true, + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + } + } + }, + "postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha512-9DHmfCZ7/hNHhIKnNkz4CU0ejtGen5BbTRJc13Z2uHfCedeCUsK2WEQoAJRBL+phs68iWK6Qf8Jze71anuysWA==", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.10", + "postcss-value-parser": "^3.1.1" + } + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha512-ma7YvxjdLQdifnc1HFsW/AW6fVfubGyR+X4bE3FOSdBVMY9bZjKVdklHT+odknKBB7FSCfKIHC3yHK7RUAqRPg==", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha512-Wgg2FS6W3AYBl+5L9poL6ZUISi5YzL+sDCJfM7zNw/Q1qsyVQXXZ2cbVui6mu2cYJpt1hOKCGj1xA4mq/obz/Q==", + "dev": true, + "requires": { + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" + }, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha512-qHJblDE2bXVRYzuDetv/wAeHOJyO97+9wxC1cdCtyzgNuSozOyRCiiLaCR1f71AN66lQdVVBipWm63V+a7bPOw==", + "dev": true, + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + } + } + }, + "postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha512-tPLZzVAiIJp46TBbpXtrUAKqedXSyW5xDEo1sikrfEfnTs+49SBZR/xDdqCiJvSSbtr615xDsaMF3RrxS2jZlA==", + "dev": true + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha512-vFSPzrJhNe6/8McOLU13XIsERohBJiIFFuC1PolgajOZdRWqRgKITP/A4Z/n4GQhEmtbxmO9NDw3QLaFfE1dFQ==", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha512-DZhT0OE+RbVqVyGsTIKx84rU/5cury1jmwPa19bViqYPQu499ZU831yMzzsyC8EhiZVd73+h5Z9xb/DdaBpw7Q==", + "dev": true, + "requires": { + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" + } + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha512-hhJdMVgP8vasrHbkKAk+ab28vEmPYgyuDzRl31V3BEB3QOR3L5TTIVEWLDNnZZ3+fiTi9d6Ker8GM8S1h8p2Ow==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" + } + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha512-e13vxPBSo3ZaPne43KVgM+UETkx3Bs4/Qvm6yXI9HQpQp4nyb7HZ0gKpkF+Wn2x+/dbQ+swNpCdZSbMOT7+TIA==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", + "dev": true, + "requires": { + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA==", + "dev": true, + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha512-RKgjEks83l8w4yEhztOwNZ+nLSrJ+NvPNhpS+mVDzoaiRHZQVoG7NF2TP5qjwnaN9YswUhj6m1E0S0Z+WDCgEQ==", + "dev": true, + "requires": { + "postcss": "^5.0.5" + } + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha512-WqtWG6GV2nELsQEFES0RzfL2ebVwmGl/M8VmMbshKto/UClBo+mznX8Zi4/hkThdqx7ijwv+O8HWPdpK7nH/Ig==", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha512-5RB1IUZhkxDCfa5fx/ogp/A82mtq+r7USqS+7zt0e428HJ7+BHCxyeY39ClmkkUtxdOd3mk8gD6d9bjH2BECMg==", + "dev": true, + "requires": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha512-0+Ow9e8JLtffjumJJFPqvN4qAvokVbdQPnijUDSOX8tfTwrILLP4ETvrZcXZxAtpFLh/U0c+q8oRMJLr1Kiu4w==", + "dev": true, + "requires": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha512-jJFrV1vWOPCQsIVitawGesRgMgunbclERQ/IRGW7r93uHrVzNQQmHQ7znsOIjJPZ4yWMzs5A8NFhp3AkPHPbDA==", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha512-lGgRqnSuAR5i5uUg1TA33r9UngfTadWxOyL2qx1KuPoCQzfmtaHjp9PuwX7yVyRxG3BWBzeFUaS5uV9eVgnEgQ==", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha512-3pqyakeGhrO0BQ5+/tGTfvi5IAUAhHRayGK8WFSu06aEv2BmHoXw/Mhb+w7VY5HERIuC+QoUI7wgrCcq2hqCVA==", + "dev": true, + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha512-y5AdQdgBoF4rbpdbeWAJuxE953g/ylRfVNp6mvAi61VCN/Y25Tu9p5mh3CyI42WbTRIiwR9a1GdFtmDnNPeskQ==", + "dev": true, + "requires": { + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" + } + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha512-WZX8r1M0+IyljoJOJleg3kYm10hxNYF9scqAT7v/xeSX1IdehutOM85SNO0gP9K+bgs86XERr7Ud5u3ch4+D8g==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha512-uhRZ2hRgj0lorxm9cr62B01YzpUe63h0RXMXQ4gWW3oa2rpJh+FJAiEAytaFCPU/VgaBS+uW2SJ1XKyDNz1h4w==", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "peer": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", + "dev": true + }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + }, + "pretty-bytes": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", + "integrity": "sha512-yJAF+AjbHKlxQ8eezMd/34Mnj/YTQ3i6kLzvVsH4l/BfIFtp444n0wVbnsn66JimZ9uBofv815aRp1zCppxlWw==", + "dev": true + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "prosemirror-collab": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.0.tgz", + "integrity": "sha512-+S/IJ69G2cUu2IM5b3PBekuxs94HO1CxJIWOFrLQXUaUDKL/JfBx+QcH31ldBlBXyDEUl+k3Vltfi1E1MKp2mA==", + "requires": { + "prosemirror-state": "^1.0.0" + } + }, + "prosemirror-commands": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.3.0.tgz", + "integrity": "sha512-BwBbZ5OAScPcm0x7H8SPbqjuEJnCU2RJT9LDyOiiIl/3NbL1nJZI4SFNHwU2e/tRr2Xe7JsptpzseqvZvToLBQ==", + "requires": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "prosemirror-dropcursor": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.5.0.tgz", + "integrity": "sha512-vy7i77ddKyXlu8kKBB3nlxLBnsWyKUmQIPB5x8RkYNh01QNp/qqGmdd5yZefJs0s3rtv5r7Izfu2qbtr+tYAMQ==", + "requires": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "prosemirror-gapcursor": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.1.tgz", + "integrity": "sha512-GKTeE7ZoMsx5uVfc51/ouwMFPq0o8YrZ7Hx4jTF4EeGbXxBveUV8CGv46mSHuBBeXGmvu50guoV2kSnOeZZnUA==", + "requires": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "prosemirror-history": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.3.0.tgz", + "integrity": "sha512-qo/9Wn4B/Bq89/YD+eNWFbAytu6dmIM85EhID+fz9Jcl9+DfGEo8TTSrRhP15+fFEoaPqpHSxlvSzSEbmlxlUA==", + "requires": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "rope-sequence": "^1.3.0" + } + }, + "prosemirror-inputrules": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.2.0.tgz", + "integrity": "sha512-eAW/M/NTSSzpCOxfR8Abw6OagdG0MiDAiWHQMQveIsZtoKVYzm0AflSPq/ymqJd56/Su1YPbwy9lM13wgHOFmQ==", + "requires": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "prosemirror-keymap": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz", + "integrity": "sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg==", + "requires": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "prosemirror-model": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.1.tgz", + "integrity": "sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==", + "requires": { + "orderedmap": "^2.0.0" + } + }, + "prosemirror-schema-list": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.1.tgz", + "integrity": "sha512-rYT4azRBZboxl54a4dRSiW0wXBEIZcMCCM9z9x0TD1jqJMm89GR16UgPNYb5+pKZ8qyti5enYN1Hhztq3KvqrQ==", + "requires": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "prosemirror-state": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz", + "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==", + "requires": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "prosemirror-tables": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.2.4.tgz", + "integrity": "sha512-0hvb436mgaOv9pyIbuL3ZbD/qCSFO9tl353yax1WMJfmgUX5iy13COWbj7x+vhUF6SmufA6kgSZz+MTsy2sJ+w==", + "requires": { + "prosemirror-keymap": "^1.1.2", + "prosemirror-model": "^1.8.1", + "prosemirror-state": "^1.3.1", + "prosemirror-transform": "^1.2.1", + "prosemirror-view": "^1.13.3" + } + }, + "prosemirror-transform": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.6.0.tgz", + "integrity": "sha512-MAp7AjsjEGEqQY0sSMufNIUuEyB1ZR9Fqlm8dTwwWwpEJRv/plsKjWXBbx52q3Ml8MtaMcd7ic14zAHVB3WaMw==", + "requires": { + "prosemirror-model": "^1.0.0" + } + }, + "prosemirror-view": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.27.0.tgz", + "integrity": "sha512-yNCQW5eiPkrMgjOT5Xa/ItIvcM7JBG7ikZKaHo26hdBW5OLNnIWGZ0BV6/OiBk742teLybLVNPCpYUcW405Ckg==", + "requires": { + "prosemirror-model": "^1.16.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "puppeteer": { + "version": "19.4.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-19.4.1.tgz", + "integrity": "sha512-PCnrR13B8A+VSEDXRmrNXRZbrkF1tfsI1hKSC7vs13eNS6CUD3Y4FA8SF8/VZy+Pm1kg5AggJT2Nu3HLAtGkFg==", + "requires": { + "cosmiconfig": "8.0.0", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "puppeteer-core": "19.4.1" + }, + "dependencies": { + "cosmiconfig": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", + "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "requires": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + } + } + } + }, + "puppeteer-core": { + "version": "19.4.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.4.1.tgz", + "integrity": "sha512-JHIuqtqrUAx4jGOTxXu4ilapV2jabxtVMA/e4wwFUMvtSsqK4nVBSI+Z1SKDoz7gRy/JUIc8WzmfocCa6SIZ1w==", + "requires": { + "cross-fetch": "3.1.5", + "debug": "4.3.4", + "devtools-protocol": "0.0.1068969", + "extract-zip": "2.0.1", + "https-proxy-agent": "5.0.1", + "proxy-from-env": "1.1.0", + "rimraf": "3.0.2", + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "8.11.0" + } + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true + } + } + }, + "react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + } + }, + "react-flow-renderer": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/react-flow-renderer/-/react-flow-renderer-10.3.12.tgz", + "integrity": "sha512-DTaz4HV0rA/qtvY80fjdb/QwIvtZEhqCQ2iAqfzFH08RjWCrLmESX4Nc400EB3CGcCK8/pDn/ta4cOS3udunTw==", + "requires": { + "@babel/runtime": "^7.18.9", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^3.7.2" + }, + "dependencies": { + "zustand": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", + "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", + "requires": {} + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + } + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha512-0dVfwYVOlf/LBA2ec4OwQ6p3X9mYxn/wOl2xTcLwjnPYrkgEfPx3VI4eGCH3rQLlPISG5v9I9bkZosKsNRTRKA==", + "dev": true, + "requires": { + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha512-STw03mQKnGUYtoNjmowo4F2cRmIIxYEGiMsjjwla/u5P1lxadj/05WkNaFjNiKTgJkj8KiXbgAiRTmcQRwQNtg==", + "dev": true + } + } + }, + "reduce-function-call": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.3.tgz", + "integrity": "sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "peer": true + }, + "regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "registry-auth-token": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "dev": true, + "requires": { + "rc": "^1.0.1" + } + }, + "regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==" + }, + "regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha512-H7AkJWMobeskkttHyhTVtS0fxpFLjxhbfMa6Bk3wimP7sdPRGL3EyCg3sAQenFfAe+xQ+oAc85Nmtvq0ROM83Q==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rifm": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/rifm/-/rifm-0.12.1.tgz", + "integrity": "sha512-OGA1Bitg/dSJtI/c4dh90svzaUPt228kzFsUkJbtA2c964IqEAwWXeL9ZJi86xWv3j5SMqRvGULl7bA6cK0Bvg==", + "requires": {} + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==", + "dev": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rope-sequence": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.3.tgz", + "integrity": "sha512-85aZYCxweiD5J8yTEbw+E6A27zSnLPNDL0WfPdw3YYodq7WjnTKo0q4dtyQ2gz23iPT8Q9CUyJtAaUNcTxRf5Q==" + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sass": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.0.tgz", + "integrity": "sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", + "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.0.1", + "neo-async": "^2.5.0", + "pify": "^4.0.1", + "semver": "^6.3.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dev": true, + "requires": { + "node-forge": "^0.10.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha512-gL8F8L4ORwsS0+iQ34yCYv///jsOq0ZL7WP55d1HnJ32o7tyFYEFQZQA22mrLIacZdU6xecaBBZ+uEiffGNyXw==", + "dev": true, + "requires": { + "semver": "^5.0.3" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", + "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "serviceworker-cache-polyfill": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz", + "integrity": "sha512-VMl1n99TbtKdO7DYNX0J9FQt1doo69V6fBniKC7o+CoJerbmFlQbsoxDa7P+b4b0tmpsdRIuzzS9sSJI7vFY2g==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.5.tgz", + "integrity": "sha512-PmPRkAYIeuRgX+ZSieViT4Z3Q23bLS2Itm/ck1tSf5P0/yVuFDiI5q9mcnpXoMdToaPSRS9MEyUx/aaBxrFzyw==", + "dev": true, + "requires": { + "debug": "^2.6.6", + "eventsource": "0.1.6", + "faye-websocket": "~0.11.0", + "inherits": "^2.0.1", + "json3": "^3.3.2", + "url-parse": "^1.1.8" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "peer": true + }, + "stylis": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", + "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha512-jT/g9FFMoe9lu2IT6HtAxTA7RR2XOrmcrmCtGnyB/+GQnV6ZjNn+KOHZbZ35yL81+1F/aB6OeEsJztzBQ2EEwA==", + "dev": true, + "requires": { + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha512-eIlkGty7HGmntbV6P/ZlAsoncFLGsNoM27lkTzS+oneY/EiNhj+geqD9ezg/ip+SW6Var0BJU2JtV0vEUZpWVQ==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + } + } + } + }, + "sw-precache": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/sw-precache/-/sw-precache-5.2.1.tgz", + "integrity": "sha512-8FAy+BP/FXE+ILfiVTt+GQJ6UEf4CVHD9OfhzH0JX+3zoy2uFk7Vn9EfXASOtVmmIVbL3jE/W8Z66VgPSZcMhw==", + "dev": true, + "requires": { + "dom-urls": "^1.1.0", + "es6-promise": "^4.0.5", + "glob": "^7.1.1", + "lodash.defaults": "^4.2.0", + "lodash.template": "^4.4.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "pretty-bytes": "^4.0.2", + "sw-toolbox": "^3.4.0", + "update-notifier": "^2.3.0" + } + }, + "sw-precache-webpack-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sw-precache-webpack-plugin/-/sw-precache-webpack-plugin-1.0.0.tgz", + "integrity": "sha512-xXda8q9Nx1uCQ3m6TfPvw6X9OcTCnl6qB/afrUi+FF9CK7Lly29XHnvRrni0fzBU/1LKToHFDfi4jpLZuhL9JQ==", + "dev": true, + "requires": { + "del": "^3.0.0", + "lodash.template": "^4.5.0", + "sw-precache": "^5.2.1", + "uglify-es": "^3.3.9" + } + }, + "sw-toolbox": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/sw-toolbox/-/sw-toolbox-3.6.0.tgz", + "integrity": "sha512-v/hu7KQQtospyDLpZxz7m5c7s90aj53YEkJ/A8x3mLPlSgIkZ6RKJkTjBG75P1p/fo5IeSA4TycyJg3VSu/aPw==", + "dev": true, + "requires": { + "path-to-regexp": "^1.0.1", + "serviceworker-cache-polyfill": "^4.0.0" + } + }, + "tapable": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", + "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==", + "dev": true + }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha512-7dPUZQGy/+m3/wjVz3ZW5dobSoD/02NxJpoXUX0WIyjfVS3l0c+b/+9phIDFA7FHzkYtwtMFgeGZ/Y8jVTeqQQ==", + "dev": true, + "requires": { + "execa": "^0.7.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "peer": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "time-stamp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.2.0.tgz", + "integrity": "sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA==", + "dev": true + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "requires": { + "@popperjs/core": "^2.9.0" + } + }, + "tiptap": { + "version": "1.32.2", + "resolved": "https://registry.npmjs.org/tiptap/-/tiptap-1.32.2.tgz", + "integrity": "sha512-5IwVj8nGo8y5V3jbdtoEd7xNUsi8Q0N6WV2Nfs70olqz3fldXkiImBrDhZJ4Anx8vhyP6PIBttrg0prFVmwIvw==", + "requires": { + "prosemirror-commands": "^1.1.4", + "prosemirror-dropcursor": "^1.3.2", + "prosemirror-gapcursor": "^1.1.5", + "prosemirror-inputrules": "^1.1.3", + "prosemirror-keymap": "^1.1.4", + "prosemirror-model": "^1.13.1", + "prosemirror-state": "^1.3.3", + "prosemirror-view": "^1.16.5", + "tiptap-commands": "^1.17.1", + "tiptap-utils": "^1.13.1" + } + }, + "tiptap-commands": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/tiptap-commands/-/tiptap-commands-1.17.1.tgz", + "integrity": "sha512-CyGvMD/c6fNer5LThWGtrVMXHAqHn93ivGQpqJ58x3HNZFuoIiF9QTWXAiWbY/4QrG0ANYHKCSe9n5afickTqw==", + "requires": { + "prosemirror-commands": "^1.1.4", + "prosemirror-inputrules": "^1.1.2", + "prosemirror-model": "^1.13.1", + "prosemirror-schema-list": "^1.1.4", + "prosemirror-state": "^1.3.3", + "prosemirror-tables": "^1.1.1", + "tiptap-utils": "^1.13.1" + } + }, + "tiptap-extensions": { + "version": "1.35.2", + "resolved": "https://registry.npmjs.org/tiptap-extensions/-/tiptap-extensions-1.35.2.tgz", + "integrity": "sha512-TIMbHVJe0/3aVeTeCmqGbatDkfxduPYFOffNCmuKR+h6oQNzTu6rLVhRzoNqktfxIoi/b44SiDPorTjSN72dCw==", + "requires": { + "lowlight": "^1.17.0", + "prosemirror-collab": "^1.2.2", + "prosemirror-history": "^1.1.3", + "prosemirror-model": "^1.13.1", + "prosemirror-state": "^1.3.3", + "prosemirror-tables": "^1.1.1", + "prosemirror-transform": "^1.2.8", + "prosemirror-view": "^1.16.5", + "tiptap": "^1.32.2", + "tiptap-commands": "^1.17.1", + "tiptap-utils": "^1.13.1" + } + }, + "tiptap-utils": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/tiptap-utils/-/tiptap-utils-1.13.1.tgz", + "integrity": "sha512-RoCvMfkdu7fp9u7nsRr1OgsYU8RFjoHKHEKpx075rJ9X0t+j5Vxah9n6QzTTr4yjvcavq22WO2flFacm36zYtA==", + "requires": { + "prosemirror-model": "^1.13.1", + "prosemirror-state": "^1.3.3", + "prosemirror-tables": "^1.1.1" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==", + "dev": true + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "peer": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "peer": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha512-qLq/4y2pjcU3vhlhseXGGJ7VbFO4pBANu0kwl8VCa9KEI0V8VfZIx2Fy3w01iSTA/pGwKZSmu/+I4etLNDdt5w==", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A==", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==", + "dev": true, + "optional": true + }, + "uglifyjs-webpack-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha512-46TFiBOzX7xq/PcSWfFwkyjpemdRnMe31UQF+os0y+1W3k95f6R4SEt02Hj4p3X0Mir9gfrkmOtshFidS0VPUg==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + } + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==", + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + } + } + }, + "unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha512-N0XH6lqDtFH84JxptQoZYmloF4nzrQqqrAymNj+/gW60AO2AZgOcf4O/nUXJcYfyQkqvMo9lSupBZmmgvuVXlw==", + "dev": true + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "dev": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", + "dev": true + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "dev": true + } + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "peer": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "vue": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.8.tgz", + "integrity": "sha512-ncwlZx5qOcn754bCu5/tS/IWPhXHopfit79cx+uIlLMyt3vCMGcXai5yCG5y+I6cDmEj4ukRYyZail9FTQh7lQ==", + "requires": { + "@vue/compiler-sfc": "2.7.8", + "csstype": "^3.1.0" + } + }, + "vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "vue-loader": { + "version": "13.7.3", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-13.7.3.tgz", + "integrity": "sha512-ACCwbfeC6HjY2pnDii+Zer+MZ6sdOtwvLmDXRK/BoD3WNR551V22R6KEagwHoTRJ0ZlIhpCBkptpCU6+Ri/05w==", + "dev": true, + "requires": { + "consolidate": "^0.14.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "lru-cache": "^4.1.1", + "postcss": "^6.0.8", + "postcss-load-config": "^1.1.0", + "postcss-selector-parser": "^2.0.0", + "prettier": "^1.7.0", + "resolve": "^1.4.0", + "source-map": "^0.6.1", + "vue-hot-reload-api": "^2.2.0", + "vue-style-loader": "^3.0.0", + "vue-template-es2015-compiler": "^1.6.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "vue-router": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.4.tgz", + "integrity": "sha512-x+/DLAJZv2mcQ7glH2oV9ze8uPwcI+H+GgTgTmb5I55bCgY3+vXWIsqbYUzbBSZnwFHEJku4eoaH/x98veyymQ==" + }, + "vue-style-loader": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-3.1.2.tgz", + "integrity": "sha512-ICtVdK/p+qXWpdSs2alWtsXt9YnDoYjQe0w5616j9+/EhjoxZkbun34uWgsMFnC1MhrMMwaWiImz3K2jK1Yp2Q==", + "dev": true, + "requires": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, + "vue-template-compiler": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.8.tgz", + "integrity": "sha512-eQqdcUpJKJpBRPDdxCNsqUoT0edNvdt1jFjtVnVS/LPPmr0BU2jWzXlrf6BVMeODtdLewB3j8j3WjNiB+V+giw==", + "requires": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "vuera": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/vuera/-/vuera-0.2.7.tgz", + "integrity": "sha512-MZErNEK+xXjxBzIDFL9cdEtX3TDZk4pMJGB+WLrlFOVro9okS/FeOSpdPFkpdyZUImZN/or4CrmztA932ChGEQ==", + "requires": {} + }, + "vuetify": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.6.12.tgz", + "integrity": "sha512-qe3hcMpWmT1O15tp+p65lOS7UKZ/hQYQktCsw9iXx2u3RwVbX6GR82gY2iROrKsiAzYDvMgrYxWQwY/pUfkekw==", + "requires": {} + }, + "vuex": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", + "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "requires": {} + }, + "w3c-keyname": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.5.tgz", + "integrity": "sha512-WJrK7i6w+ULuZsGscCezbCH4Aev5U3xY87vnSimzzEgPQhb0Sa0a1rE3c2jtEwrFtSfi61Jefw3jI5/DD/3jbQ==" + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dev": true, + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "dev": true, + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "optional": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "webpack": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", + "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", + "dev": true, + "requires": { + "acorn": "^5.0.0", + "acorn-dynamic-import": "^2.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "async": "^2.1.2", + "enhanced-resolve": "^3.4.0", + "escope": "^3.6.0", + "interpret": "^1.0.0", + "json-loader": "^0.5.4", + "json5": "^0.5.1", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "mkdirp": "~0.5.0", + "node-libs-browser": "^2.0.0", + "source-map": "^0.5.3", + "supports-color": "^4.2.1", + "tapable": "^0.2.7", + "uglifyjs-webpack-plugin": "^0.4.6", + "watchpack": "^1.4.0", + "webpack-sources": "^1.0.1", + "yargs": "^8.0.2" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha512-ycQR/UbvI9xIlEdQT1TQqwoXtEldExbCEAJgRo5YXlmSKjv6ThHnP9/vwGa1gr19Gfw+LkFd7KqYMhzrRC5JYw==", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + }, + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha512-TNM20HMW67kxHRNCZdvLyiwE1ST6WyY5Ae+TG55V81NpvNwJ9+V4/po4LHA1R9afV/WrqzfedG2UJCk2+swirw==", + "dev": true, + "requires": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + } + } + } + }, + "webpack-dev-middleware": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", + "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", + "dev": true, + "requires": { + "memory-fs": "~0.4.1", + "mime": "^1.5.0", + "path-is-absolute": "^1.0.0", + "range-parser": "^1.0.3", + "time-stamp": "^2.0.0" + } + }, + "webpack-dev-server": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz", + "integrity": "sha512-7TdOKKt7G3sWEhPKV0zP+nD0c4V9YKUJ3wDdBwQsZNo58oZIRoVIu66pg7PYkBW8A74msP9C2kLwmxGHndz/pw==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "array-includes": "^3.0.3", + "bonjour": "^3.5.0", + "chokidar": "^2.1.2", + "compression": "^1.7.3", + "connect-history-api-fallback": "^1.3.0", + "debug": "^3.1.0", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "^0.19.1", + "import-local": "^1.0.0", + "internal-ip": "1.2.0", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "selfsigned": "^1.9.1", + "serve-index": "^1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.1.5", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^5.1.0", + "webpack-dev-middleware": "1.12.2", + "yargs": "6.6.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + } + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", + "dev": true + }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha512-6/QWTdisjnu5UHUzQGst+UOEuEVwIzFVGBjq3jMTFNs5WJQsH/X6nMURSaScIdF5txylr1Ao9bvbWiKi2yXbwA==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^4.2.0" + } + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha512-+QQWqC2xeL0N5/TE+TY6OGEqyNRM+g2/r712PDNYgiCdXYCApXf1vzfmDSLBxfGRwV+moTq/V8FnMI24JCm2Yg==", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha512-mmIPAft2vTgEILgPeZFqE/wWh24SEsR/k+N9fJ3Jxrz44iDFy9aemCxdksfURSHYFCLmvs/d/7Iso5XjPpNfrA==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "dev": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "peer": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha512-1Dly4xqlulvPD3fZUQJLY+FUIeqN3N2MM3uqe4rCJftAvOjFa3jFGfctOgluGx4ahPbUCsZkmJILiP0Vi4T6lQ==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha512-3RiZrpLpjrzIAKgGdPktBcMP/eG5bDFlkI+PHle1qwzyVXyDQL+pD/eZaMoOOO0Y7LLBfjpucObuUm/icvbpKQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha512-WhzC+xgstid9MbVUktco/bf+KJG+Uu6vMX0LN1sLJvwmbCQVxb4D8LzogobonKycNasCZLdOzTAk1SK7+K7swg==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "dev": true + } + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "zustand": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.0.0.tgz", + "integrity": "sha512-OrsfQTnRXF1LZ9/vR/IqN9ws5EXUhb149xmPjErZnUrkgxS/gAHGy2dPNIVkVvoxrVe1sIydn4JjF0dYHmGeeQ==", + "requires": { + "use-sync-external-store": "1.2.0" + } + } + } +} diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json new file mode 100644 index 0000000000..9eb7fec088 --- /dev/null +++ b/apps/dashboard/package.json @@ -0,0 +1,83 @@ +{ + "name": "hello01", + "description": "testing vue", + "version": "1.0.0", + "author": "ankush ", + "license": "MIT", + "private": true, + "scripts": { + "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot --config web/webpack.config.js", + "build": "cross-env NODE_ENV=production webpack --progress --hide-modules --config web/webpack.config.js", + "hot": "webpack --watch --progress --config web/webpack.config.js" + }, + "dependencies": { + "@babel/core": "^7.13.10", + "@babel/eslint-parser": "^7.13.10", + "@babel/plugin-proposal-object-rest-spread": "^7.13.8", + "@babel/plugin-transform-react-jsx": "7.12.11", + "@babel/plugin-transform-runtime": "^7.13.10", + "@babel/preset-env": "^7.13.10", + "@babel/preset-react": "7.0.0", + "@emotion/react": "^11.9.3", + "@emotion/styled": "^11.9.3", + "@fortawesome/fontawesome-svg-core": "^1.2.36", + "@fortawesome/free-brands-svg-icons": "^5.15.3", + "@fortawesome/free-regular-svg-icons": "^5.15.3", + "@fortawesome/free-solid-svg-icons": "^5.15.4", + "@fortawesome/react-fontawesome": "^0.2.0", + "@fortawesome/vue-fontawesome": "^2.0.2", + "@material-ui/core": "^4.12.4", + "@mdi/font": "^5.6.55", + "@mui/material": "^5.9.2", + "@mui/x-date-pickers": "^5.0.0", + "@puppeteer/replay": "^2.7.1", + "are-you-es5": "^2.1.1", + "axios": "^0.21.1", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "dayjs": "^1.11.5", + "fiber": "^1.0.4", + "file-saver": "^2.0.5", + "highcharts": "^9.0.1", + "highcharts-vue": "^1.0.4", + "puppeteer": "^19.4.1", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-flow-renderer": "^10.3.12", + "tippy.js": "^6.3.1", + "tiptap": "^1.32.1", + "tiptap-extensions": "^1.35.1", + "vue": "^2.5.11", + "vue-router": "^3.5.1", + "vuera": "^0.2.7", + "vuetify": "^2.4.3", + "vuex": "^3.6.2", + "zustand": "^4.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ], + "devDependencies": { + "babel-core": "^6.0.0", + "babel-eslint": "^7.0.0", + "babel-loader": "^8.2.2", + "babel-plugin-transform-regenerator": "^6.26.0", + "babel-plugin-transform-runtime": "^6.0.0", + "babel-polyfill": "^6.26.0", + "babel-preset-es2015": "^6.0.0", + "babel-preset-stage-2": "^6.0.0", + "babel-register": "^6.0.0", + "cross-env": "^5.0.5", + "css-loader": "^0.28.7", + "file-loader": "^1.1.4", + "sass": "^1.25.0", + "sass-loader": "^7.2.0", + "sw-precache-webpack-plugin": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.3.0", + "vue-loader": "^13.0.5", + "vue-template-compiler": "^2.4.4", + "webpack": "^3.6.0", + "webpack-dev-server": "^2.9.1" + } +} diff --git a/apps/dashboard/pom.xml b/apps/dashboard/pom.xml new file mode 100644 index 0000000000..96bf8ebc78 --- /dev/null +++ b/apps/dashboard/pom.xml @@ -0,0 +1,254 @@ + + 4.0.0 + + + com.akto.apps + apps + ${revision} + + + + com.akto.apps.dashboard + dashboard + war + + + + com.amazonaws + aws-java-sdk-bom + 1.11.678 + pom + import + + + + + + + com.amazonaws + aws-java-sdk-lambda + 1.11.678 + + + com.amazonaws + aws-java-sdk-logs + 1.11.678 + + + com.amazonaws + aws-java-sdk-elasticloadbalancingv2 + 1.11.678 + + + com.amazonaws + aws-java-sdk-cloudformation + 1.11.678 + + + org.apache.struts + struts2-core + 2.5.30 + + + org.apache.commons + commons-lang3 + 3.12.0 + + + + org.eclipse.jetty + jetty-servlets + 9.4.44.v20210927 + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + org.mongodb + mongodb-driver-sync + 4.2.1 + + + com.akto.apps.api-runtime + api-runtime + ${project.version} + + + com.akto.libs.dao + dao + ${project.version} + + + com.akto.libs.utils + utils + ${project.version} + + + com.akto.libs.integrations + integrations + ${project.version} + + + com.akto.apps.testing + testing + ${project.version} + + + org.apache.struts + struts2-json-plugin + 2.5.30 + + + io.jsonwebtoken + jjwt-api + 0.11.2 + + + io.jsonwebtoken + jjwt-impl + 0.11.2 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.2 + runtime + + + com.slack.api + slack-api-client + 1.7.1 + + + com.sendgrid + sendgrid-java + 4.7.1 + + + com.twilio.sdk + twilio + 8.8.0 + + + + com.fasterxml.jackson.core + jackson-databind + 2.12.7.1 + + + com.fasterxml.jackson.core + jackson-core + 2.13.0 + + + com.fasterxml.jackson.core + jackson-annotations + 2.13.0 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.13.0 + + + + com.googlecode.owasp-java-html-sanitizer + owasp-java-html-sanitizer + 20211018.1 + + + com.google.api-client + google-api-client + 1.23.0 + + + com.google.oauth-client + google-oauth-client-jetty + 1.23.0 + + + com.google.apis + google-api-services-sheets + v4-rev493-1.23.0 + + + com.google.apis + google-api-services-drive + v3-rev197-1.25.0 + + + io.micrometer + micrometer-registry-prometheus + 1.8.1 + + + + com.github.vladimir-bukhtoyarov + bucket4j-core + 7.5.0 + + + com.akto.libs.utils + utils + test-jar + ${project.version} + test + + + + src/main/java + src/test/java + + + src/main/resources + true + + **/version.txt + + + + src/main/resources + false + + **/version.txt + + + + + + maven-war-plugin + 2.3 + + web + false + + + + org.eclipse.jetty + jetty-maven-plugin + 9.4.36.v20210114 + + web + + / + WEB-INF/web.xml + + + + + maven-compiler-plugin + org.apache.maven.plugins + + 8 + 8 + + + + ${project.artifactId} + + diff --git a/apps/dashboard/src/main/DockerFile b/apps/dashboard/src/main/DockerFile new file mode 100644 index 0000000000..2da83ac240 --- /dev/null +++ b/apps/dashboard/src/main/DockerFile @@ -0,0 +1,3 @@ +FROM jetty:9.4-jre8 +ADD ./target/Struts2XMLHelloWorld.war /var/lib/jetty/webapps/root.war +EXPOSE 8080 \ No newline at end of file diff --git a/apps/dashboard/src/main/java/com/akto/action/APICatalogAction.java b/apps/dashboard/src/main/java/com/akto/action/APICatalogAction.java new file mode 100644 index 0000000000..dd1738fec6 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/APICatalogAction.java @@ -0,0 +1,31 @@ +package com.akto.action; + +import java.util.List; + +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dto.type.SingleTypeInfo; +import com.opensymphony.xwork2.Action; + +public class APICatalogAction { + + List apiCatalogData; + + public String getAPICatalog() { + + try { + apiCatalogData = SingleTypeInfoDao.instance.fetchAll(); + return Action.SUCCESS.toUpperCase(); + + } catch (Exception e) { + return Action.ERROR.toUpperCase(); + } + } + + public List getApiCatalogData() { + return this.apiCatalogData; + } + + public void setApiCatalogData(List apiCatalogData) { + this.apiCatalogData = apiCatalogData; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java b/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java new file mode 100644 index 0000000000..2d818ea028 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/AccessTokenAction.java @@ -0,0 +1,106 @@ +package com.akto.action; + +import com.akto.dao.UsersDao; +import com.akto.dto.User; +import com.akto.utils.HttpUtils; +import com.akto.utils.Token; +import com.opensymphony.xwork2.Action; +import org.apache.struts2.interceptor.ServletRequestAware; +import org.apache.struts2.interceptor.ServletResponseAware; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.List; + +import static com.akto.action.LoginAction.REFRESH_TOKEN_COOKIE_NAME; + +public class AccessTokenAction implements Action, ServletResponseAware, ServletRequestAware { + public static final String ACCESS_TOKEN_HEADER_NAME = "access-token"; + @Override + public String execute() { + Token token = generateAccessTokenFromServletRequest(servletRequest); + if (token == null) { + Cookie cookie = generateDeleteCookie(); + servletResponse.addCookie(cookie); + return Action.ERROR.toUpperCase(); + } + String accessToken = token.getAccessToken(); + + // deepcode ignore HttpResponseSplitting: + servletResponse.setHeader(ACCESS_TOKEN_HEADER_NAME, accessToken); + + return Action.SUCCESS.toUpperCase(); + } + + public static Cookie generateDeleteCookie() { + Cookie cookie = new Cookie(REFRESH_TOKEN_COOKIE_NAME, null); + cookie.setMaxAge(0); + cookie.setHttpOnly(true); + cookie.setPath("/dashboard"); + cookie.setSecure(HttpUtils.isHttpsEnabled()); + return cookie; + } + + public static Token generateAccessTokenFromServletRequest(HttpServletRequest httpServletRequest) { + + Cookie[] cookies = httpServletRequest.getCookies(); + + if (cookies == null) { + return null; + } + + String refreshToken = null; + for (Cookie cookie : cookies) { + if (cookie.getName().equals("refreshToken")) { + refreshToken = cookie.getValue(); + break; + } + } + + if (refreshToken == null) { + return null; + } + + Token token ; + try { + token = new Token(refreshToken); + } catch (Exception e) { + return null; + } + + if (token.getSignedUp().equalsIgnoreCase("false")) { + return token; + } + + String username = token.getUsername(); + User user = UsersDao.instance.findOne("login", username); + if (user == null) { + return null; + } + + List refreshTokens = user.getRefreshTokens(); + if (refreshTokens != null && refreshTokens.contains(refreshToken)) { + return token; + } else { + + return null; + } + + } + + protected HttpServletResponse servletResponse; + @Override + public void setServletResponse(HttpServletResponse httpServletResponse) { + this.servletResponse= httpServletResponse; + } + + protected HttpServletRequest servletRequest; + @Override + public void setServletRequest(HttpServletRequest httpServletRequest) { + this.servletRequest = httpServletRequest; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/AccountAction.java b/apps/dashboard/src/main/java/com/akto/action/AccountAction.java new file mode 100644 index 0000000000..c9a5395086 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/AccountAction.java @@ -0,0 +1,173 @@ +package com.akto.action; + +import com.akto.dao.AccountsDao; +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.Account; +import com.akto.dto.UserAccountEntry; +import com.akto.utils.cloud.serverless.aws.Lambda; +import com.akto.utils.cloud.stack.Stack; +import com.akto.utils.cloud.stack.aws.AwsStack; +import com.akto.utils.cloud.stack.dto.StackState; +import com.akto.utils.platform.MirroringStackDetails; +import com.amazonaws.services.lambda.model.*; +import com.mongodb.BasicDBObject; +import com.opensymphony.xwork2.Action; +import com.amazonaws.services.lambda.AWSLambda; +import com.amazonaws.services.lambda.AWSLambdaClientBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static com.mongodb.client.model.Filters.eq; + +public class AccountAction extends UserAction { + + private String newAccountName; + private int newAccountId; + private static final Logger logger = LoggerFactory.getLogger(AccountAction.class); + + public static final int MAX_NUM_OF_LAMBDAS_TO_FETCH = 50; + + @Override + public String execute() { + + return Action.SUCCESS.toUpperCase(); + } + + private void invokeExactLambda(String functionName, AWSLambda awsLambda) { + + InvokeRequest invokeRequest = new InvokeRequest() + .withFunctionName(functionName) + .withPayload("{}"); + InvokeResult invokeResult = null; + try { + + logger.info("Invoke lambda "+functionName); + invokeResult = awsLambda.invoke(invokeRequest); + + String resp = new String(invokeResult.getPayload().array(), StandardCharsets.UTF_8); + logger.info("Function: {}, response: {}", functionName, resp); + } catch (AWSLambdaException e) { + logger.error(String.format("Error while invoking Lambda: %s", functionName), e); + } + } + + private void listMatchingLambda(String functionName) { + AWSLambda awsLambda = AWSLambdaClientBuilder.standard().build(); + try { + ListFunctionsRequest request = new ListFunctionsRequest(); + request.setMaxItems(MAX_NUM_OF_LAMBDAS_TO_FETCH); + + boolean done = false; + while(!done){ + ListFunctionsResult functionResult = awsLambda + .listFunctions(request); + List list = functionResult.getFunctions(); + logger.info("Found {} functions", list.size()); + + for (FunctionConfiguration config: list) { + logger.info("Found function: {}",config.getFunctionName()); + + if(config.getFunctionName().contains(functionName)) { + logger.info("Invoking function: {}", config.getFunctionName()); + invokeExactLambda(config.getFunctionName(), awsLambda); + } + } + + if(functionResult.getNextMarker() == null){ + done = true; + } + request = new ListFunctionsRequest(); + request.setMaxItems(MAX_NUM_OF_LAMBDAS_TO_FETCH); + request.setMarker(functionResult.getNextMarker()); + } + + + } catch (AWSLambdaException e) { + logger.error("Error while updating Akto",e); + } + } + + public String takeUpdate() { + if(checkIfStairwayInstallation()) { + logger.info("This is a stairway installation, invoking lambdas now"); + String lambda; + try { + lambda = AwsStack.getInstance().fetchResourcePhysicalIdByLogicalId(MirroringStackDetails.getStackName(), MirroringStackDetails.AKTO_CONTEXT_ANALYZER_UPDATE_LAMBDA); + Lambda.getInstance().invokeFunction(lambda); + logger.info("Successfully invoked lambda {}", lambda); + } catch (Exception e) { + logger.error("Failed to update Akto Context Analyzer", e); + } + try{ + lambda = AwsStack.getInstance().fetchResourcePhysicalIdByLogicalId(MirroringStackDetails.getStackName(),MirroringStackDetails.AKTO_DASHBOARD_UPDATE_LAMBDA); + Lambda.getInstance().invokeFunction(lambda); + logger.info("Successfully invoked lambda {}", lambda); + } catch (Exception e) { + logger.error("Failed to update Akto Dashboard", e); + } + + try{ + lambda = AwsStack.getInstance().fetchResourcePhysicalIdByLogicalId(MirroringStackDetails.getStackName(), MirroringStackDetails.AKTO_RUNTIME_UPDATE_LAMBDA); + Lambda.getInstance().invokeFunction(lambda); + logger.info("Successfully invoked lambda {}", lambda); + } catch (Exception e) { + logger.error("Failed to update Akto Traffic Mirroring Instance", e); + } + } else { + logger.info("This is an old installation, updating via old way"); + listMatchingLambda("InstanceRefresh"); + } + + return Action.SUCCESS.toUpperCase(); + } + + private boolean checkIfStairwayInstallation() { + StackState stackStatus = AwsStack.getInstance().fetchStackStatus(MirroringStackDetails.getStackName()); + return "CREATE_COMPLETE".equalsIgnoreCase(stackStatus.getStatus().toString()); + } + + public String createNewAccount() { + newAccountId = Context.getId(); + AccountsDao.instance.insertOne(new Account(newAccountId, newAccountName)); + + UserAccountEntry uae = new UserAccountEntry(); + uae.setAccountId(newAccountId); + BasicDBObject set = new BasicDBObject("$set", new BasicDBObject("accounts."+newAccountId, uae)); + + UsersDao.instance.getMCollection().updateOne(eq("login", getSUser().getLogin()), set); + + getSession().put("accountId", newAccountId); + Context.accountId.set(newAccountId); + + return Action.SUCCESS.toUpperCase(); + } + + public String goToAccount() { + if (getSUser().getAccounts().containsKey(newAccountId+"")) { + getSession().put("accountId", newAccountId); + Context.accountId.set(newAccountId); + return SUCCESS.toUpperCase(); + } + + return ERROR.toUpperCase(); + } + + public String getNewAccountName() { + return newAccountName; + } + + public void setNewAccountName(String newAccountName) { + this.newAccountName = newAccountName; + } + + public int getNewAccountId() { + return newAccountId; + } + + public void setNewAccountId(int newAccountId) { + this.newAccountId = newAccountId; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/AdminSettingsAction.java b/apps/dashboard/src/main/java/com/akto/action/AdminSettingsAction.java new file mode 100644 index 0000000000..3d2575a893 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/AdminSettingsAction.java @@ -0,0 +1,107 @@ +package com.akto.action; + +import com.akto.dao.*; +import com.akto.dao.context.Context; +import com.akto.dto.*; +import com.akto.runtime.Main; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.Updates; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class AdminSettingsAction extends UserAction { + + AccountSettings accountSettings; + + private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + @Override + public String execute() throws Exception { + accountSettings = AccountSettingsDao.instance.findOne(AccountSettingsDao.generateFilter()); + return SUCCESS.toUpperCase(); + } + + public AccountSettings.SetupType setupType; + public String updateSetupType() { + AccountSettingsDao.instance.getMCollection().updateOne( + AccountSettingsDao.generateFilter(), + Updates.set(AccountSettings.SETUP_TYPE, this.setupType), + new UpdateOptions().upsert(true) + ); + + return SUCCESS.toUpperCase(); + } + + public String updateMergeAsyncOutside() { + User user = getSUser(); + if (user == null) return ERROR.toUpperCase(); + + AccountSettingsDao.instance.getMCollection().updateOne( + AccountSettingsDao.generateFilter(), + Updates.set(AccountSettings.MERGE_ASYNC_OUTSIDE, true), + new UpdateOptions().upsert(true) + ); + + return SUCCESS.toUpperCase(); + } + + private boolean redactPayload; + public String toggleRedactFeature() { + User user = getSUser(); + if (user == null) return ERROR.toUpperCase(); + boolean isAdmin = RBACDao.instance.isAdmin(user.getId()); + if (!isAdmin) return ERROR.toUpperCase(); + + AccountSettingsDao.instance.getMCollection().updateOne( + AccountSettingsDao.generateFilter(), + Updates.combine( + Updates.set(AccountSettings.REDACT_PAYLOAD, redactPayload), + Updates.set(AccountSettings.SAMPLE_DATA_COLLECTION_DROPPED, false) + ), + new UpdateOptions().upsert(true) + ); + + + if (!redactPayload) return SUCCESS.toUpperCase(); + + dropCollectionsInitial(Context.accountId.get()); + + int accountId = Context.accountId.get(); + + executorService.schedule( new Runnable() { + public void run() { + dropCollections(accountId); + } + }, 3*Main.sync_threshold_time, TimeUnit.SECONDS); + + return SUCCESS.toUpperCase(); + } + + private static void dropCollectionsInitial(int accountId) { + Context.accountId.set(accountId); + SampleDataDao.instance.getMCollection().drop(); + FilterSampleDataDao.instance.getMCollection().drop(); + SensitiveSampleDataDao.instance.getMCollection().drop(); + SingleTypeInfoDao.instance.deleteValues(); + } + + public static void dropCollections(int accountId) { + dropCollectionsInitial(accountId); + AccountSettingsDao.instance.getMCollection().updateOne( + AccountSettingsDao.generateFilter(), Updates.set(AccountSettings.SAMPLE_DATA_COLLECTION_DROPPED, true), new UpdateOptions().upsert(true) + ); + } + + public AccountSettings getAccountSettings() { + return this.accountSettings; + } + + public void setRedactPayload(boolean redactPayload) { + this.redactPayload = redactPayload; + } + + public void setSetupType(AccountSettings.SetupType setupType) { + this.setupType = setupType; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java new file mode 100644 index 0000000000..fbc8b007a6 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/ApiCollectionsAction.java @@ -0,0 +1,125 @@ +package com.akto.action; + +import java.util.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import com.akto.dao.APISpecDao; +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.SensitiveParamInfoDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.context.Context; +import com.akto.dto.ApiCollection; +import com.mongodb.client.model.Filters; +import com.mongodb.BasicDBObject; +import com.opensymphony.xwork2.Action; + +public class ApiCollectionsAction extends UserAction { + + List apiCollections = new ArrayList<>(); + int apiCollectionId; + + public String fetchAllCollections() { + this.apiCollections = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + + Map countMap = ApiCollectionsDao.instance.buildEndpointsCountToApiCollectionMap(); + + for (ApiCollection apiCollection: apiCollections) { + int apiCollectionId = apiCollection.getId(); + Integer count = countMap.get(apiCollectionId); + if (count != null && apiCollection.getHostName() != null) { + apiCollection.setUrlsCount(count); + } else { + apiCollection.setUrlsCount(apiCollection.getUrls().size()); + } + } + + return Action.SUCCESS.toUpperCase(); + } + + static int maxCollectionNameLength = 25; + private String collectionName; + public String createCollection() { + if (this.collectionName == null) { + addActionError("Invalid collection name"); + return ERROR.toUpperCase(); + } + + if (this.collectionName.length() > maxCollectionNameLength) { + addActionError("Custom collections max length: " + maxCollectionNameLength); + return ERROR.toUpperCase(); + } + + for (char c: this.collectionName.toCharArray()) { + boolean alphabets = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + boolean numbers = c >= '0' && c <= '9'; + boolean specialChars = c == '-' || c == '.' || c == '_'; + boolean spaces = c == ' '; + + if (!(alphabets || numbers || specialChars || spaces)) { + addActionError("Collection names can only be alphanumeric and contain '-','.' and '_'"); + return ERROR.toUpperCase(); + } + } + + // unique names + ApiCollection sameNameCollection = ApiCollectionsDao.instance.findByName(collectionName); + if (sameNameCollection != null){ + addActionError("Collection names must be unique"); + return ERROR.toUpperCase(); + } + + // do not change hostName or vxlanId here + ApiCollection apiCollection = new ApiCollection(Context.now(), collectionName,Context.now(),new HashSet<>(), null, 0); + ApiCollectionsDao.instance.insertOne(apiCollection); + this.apiCollections = new ArrayList<>(); + this.apiCollections.add(apiCollection); + return Action.SUCCESS.toUpperCase(); + } + + public String deleteCollection() { + + this.apiCollections = new ArrayList<>(); + this.apiCollections.add(new ApiCollection(apiCollectionId, null, 0, null, null, 0)); + return this.deleteMultipleCollections(); + } + + public String deleteMultipleCollections() { + List apiCollectionIds = new ArrayList<>(); + for(ApiCollection apiCollection: this.apiCollections) { + if(apiCollection.getId() == 0) { + continue; + } + apiCollectionIds.add(apiCollection.getId()); + } + + ApiCollectionsDao.instance.deleteAll(Filters.in("_id", apiCollectionIds)); + SingleTypeInfoDao.instance.deleteAll(Filters.in("apiCollectionId", apiCollectionIds)); + APISpecDao.instance.deleteAll(Filters.in("apiCollectionId", apiCollectionIds)); + SensitiveParamInfoDao.instance.deleteAll(Filters.in("apiCollectionId", apiCollectionIds)); + + return SUCCESS.toUpperCase(); + } + + public List getApiCollections() { + return this.apiCollections; + } + + public void setApiCollections(List apiCollections) { + this.apiCollections = apiCollections; + } + + public void setCollectionName(String collectionName) { + this.collectionName = collectionName; + } + + public int getApiCollectionId() { + return this.apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/action/ApiInfoAction.java b/apps/dashboard/src/main/java/com/akto/action/ApiInfoAction.java new file mode 100644 index 0000000000..e57ff6dbac --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/ApiInfoAction.java @@ -0,0 +1,41 @@ +package com.akto.action; + + +import com.akto.action.observe.InventoryAction; +import com.akto.dao.ApiInfoDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.ApiInfo.ApiInfoKey; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods.Method; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class ApiInfoAction extends UserAction { + @Override + public String execute() { + return SUCCESS; + } + + private List apiInfoList; + private int apiCollectionId; + public String fetchApiInfoList() { + apiInfoList= ApiInfoDao.instance.findAll(Filters.eq("_id.apiCollectionId", apiCollectionId)); + for (ApiInfo apiInfo: apiInfoList) { + apiInfo.calculateActualAuth(); + } + return SUCCESS.toUpperCase(); + } + + public List getApiInfoList() { + return apiInfoList; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/ApiTokenAction.java b/apps/dashboard/src/main/java/com/akto/action/ApiTokenAction.java new file mode 100644 index 0000000000..c571580ca5 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/ApiTokenAction.java @@ -0,0 +1,169 @@ +package com.akto.action; + +import com.akto.dao.ApiTokensDao; +import com.akto.dao.context.Context; +import com.akto.dao.notifications.SlackWebhooksDao; +import com.akto.dto.ApiToken; +import com.akto.dto.ApiToken.Utility; +import com.akto.dto.notifications.SlackWebhook; +import com.akto.dto.type.KeyTypes; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.utils.RandomString; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.result.DeleteResult; +import com.opensymphony.xwork2.Action; + +import org.apache.struts2.interceptor.ServletRequestAware; + +import javax.servlet.http.HttpServletRequest; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class ApiTokenAction extends UserAction implements ServletRequestAware { + private static final int keyLength = 40; + private static final RandomString randomString = new RandomString(keyLength); + + public String addBurpToken() { + String username = getSUser().getLogin(); + String apiKey = randomString.nextString(); + if (apiKey == null || apiKey.length() != keyLength) return ERROR.toUpperCase(); + + ApiToken apiToken = new ApiToken(Context.now(),Context.accountId.get(),"burp_key",apiKey, Context.now(), username, ApiToken.Utility.BURP); + ApiTokensDao.instance.insertOne(apiToken); + apiTokenList = new ArrayList<>(); + apiTokenList.add(apiToken); + return SUCCESS.toUpperCase(); + } + + public String addExternalApiToken() { + String username = getSUser().getLogin(); + String apiKey = randomString.nextString(); + if (apiKey == null || apiKey.length() != keyLength) return ERROR.toUpperCase(); + + ApiToken apiToken = new ApiToken(Context.now(),Context.accountId.get(),"external_key",apiKey, Context.now(), + username, ApiToken.Utility.EXTERNAL_API); + ApiTokensDao.instance.insertOne(apiToken); + apiTokenList = new ArrayList<>(); + apiTokenList.add(apiToken); + return SUCCESS.toUpperCase(); + } + + private int apiTokenId; + private boolean apiTokenDeleted; + public String deleteApiToken() { + String username = getSUser().getLogin(); + DeleteResult deleteResult = ApiTokensDao.instance.getMCollection().deleteOne( + Filters.and( + Filters.eq("_id", apiTokenId), + Filters.eq(ApiToken.USER_NAME, username) + ) + ); + + long c = deleteResult.getDeletedCount(); + this.apiTokenDeleted = c >= 1; + + return SUCCESS.toUpperCase(); + } + + List apiTokenList; + public String fetchApiTokens() { + String username = getSUser().getLogin(); + apiTokenList = ApiTokensDao.instance.findAll( + Filters.and( + Filters.eq(ApiToken.USER_NAME, username) + ) + ); + + List slackWebhooks = SlackWebhooksDao.instance.findAll(new BasicDBObject()); + for(SlackWebhook sw: slackWebhooks) { + ApiToken slackToken = + new ApiToken(sw.getId(), Context.accountId.get(), sw.getWebhook(), sw.getWebhook(), + sw.getId(), sw.getUserEmail(), Utility.SLACK); + + apiTokenList.add(slackToken); + } + + return SUCCESS.toUpperCase(); + } + + private String error; + private String webhookUrl; + private String dashboardUrl; + private int frequencyInSeconds; + public int getFrequencyInSeconds() { + return frequencyInSeconds; + } + + public void setFrequencyInSeconds(int frequencyInSeconds) { + this.frequencyInSeconds = frequencyInSeconds; + } + + public String addSlackWebhook() { + + boolean isUrl = KeyTypes.patternToSubType.get(SingleTypeInfo.URL).matcher(webhookUrl).matches(); + boolean isSlackUrl = isUrl && webhookUrl.contains("hooks.slack.com"); + boolean alreadyExists = SlackWebhooksDao.instance.findOne(new BasicDBObject("webhook", webhookUrl)) != null; + + if (!isSlackUrl) { + this.error = "Please enter a valid Slack url"; + } else if (alreadyExists) { + this.error = "This webhook url already exists"; + } else { + int now = Context.now(); + + setFrequencyInSeconds(24*60*60); // set initially to one day + + SlackWebhook newWebhook = new SlackWebhook(now, webhookUrl, 1, 1, now, getSUser().getLogin(), dashboardUrl,now,frequencyInSeconds); + this.apiTokenId = SlackWebhooksDao.instance.insertOne(newWebhook).getInsertedId().asInt32().getValue(); + } + + return Action.SUCCESS.toUpperCase(); + } + + public String deleteSlackWebhook() { + SlackWebhooksDao.instance.deleteAll(new BasicDBObject("_id", apiTokenId)); + return Action.SUCCESS.toUpperCase(); + } + + public void setError(String error) { + this.error = error; + } + + public String getError() { + return this.error; + } + + public String getWebhookUrl() { + return this.webhookUrl; + } + + public void setWebhookUrl(String webhookUrl) { + this.webhookUrl = webhookUrl; + } + + public List getApiTokenList() { + return apiTokenList; + } + + public int getApiTokenId() { + return this.apiTokenId; + } + + public void setApiTokenId(int apiTokenId) { + this.apiTokenId = apiTokenId; + } + + public boolean isApiTokenDeleted() { + return apiTokenDeleted; + } + + @Override + public void setServletRequest(HttpServletRequest request) { + this.dashboardUrl = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort(); + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/BurpJarAction.java b/apps/dashboard/src/main/java/com/akto/action/BurpJarAction.java new file mode 100644 index 0000000000..d282e56b20 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/BurpJarAction.java @@ -0,0 +1,216 @@ +package com.akto.action; + +import com.akto.dao.ApiTokensDao; +import com.akto.dao.BurpPluginInfoDao; +import com.akto.dao.context.Context; +import com.akto.dto.ApiToken; +import com.akto.dto.BurpPluginInfo; +import com.akto.listener.InitializerListener; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; + +import kotlin.text.Charsets; +import org.apache.commons.io.FileUtils; +import org.apache.struts2.interceptor.ServletRequestAware; +import org.apache.struts2.interceptor.ServletResponseAware; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +public class BurpJarAction extends UserAction implements ServletResponseAware, ServletRequestAware { + + + @Override + public String execute() { + String host = servletRequest.getHeader("Origin"); + + ApiToken apiToken = ApiTokensDao.instance.findOne( + Filters.and( + Filters.eq(ApiToken.USER_NAME, getSUser().getLogin()), + Filters.eq(ApiToken.UTILITY, ApiToken.Utility.BURP) + ) + ); + + // if api token not found generate one + if (apiToken == null) { + ApiTokenAction apiTokenAction = new ApiTokenAction(); + apiTokenAction.setSession(this.getSession()); + apiTokenAction.addBurpToken(); + List apiTokenList = apiTokenAction.getApiTokenList(); + if (apiTokenList == null || apiTokenList.isEmpty()) { + addActionError("Couldn't generate burp token"); + return ERROR.toUpperCase(); + } + apiToken = apiTokenList.get(0); + } + + String token = apiToken.getKey(); + String collectionName = "Burp"; + int version = InitializerListener.burpPluginVersion; + + File tmpJarFile; + try { + tmpJarFile = File.createTempFile("temp", "jar"); + } catch (IOException e) { + addActionError("Failed creating temp file"); + return ERROR.toUpperCase(); + } + + URL url = this.getClass().getResource("/Akto.jar"); + if (url == null) { + addActionError("Akto plugin not found!"); + return ERROR.toUpperCase(); + } + + JarFile jarFile; + try { + jarFile = new JarFile(url.getPath()); + } catch (IOException e) { + addActionError("Failed creating JAR file"); + return ERROR.toUpperCase(); + } + + File credFile; + try { + credFile = File.createTempFile("creds", "txt"); // todo: remove + FileUtils.writeStringToFile(credFile,host + "\n" + token + "\n" + collectionName + "\n" + version, Charsets.UTF_8); + } catch (Exception e) { + addActionError("Failed adding credentials"); + return ERROR.toUpperCase(); + } + + try { + try (JarOutputStream tempJarOutputStream = new JarOutputStream(Files.newOutputStream(tmpJarFile.toPath()))) { + Enumeration jarEntries = jarFile.entries(); + Set done = new HashSet<>(); + + // copy existing elements + while (jarEntries.hasMoreElements()) { + JarEntry entry = jarEntries.nextElement(); + if (done.contains(entry.getName())) continue; + done.add(entry.getName()); + + InputStream entryInputStream = jarFile.getInputStream(entry); + tempJarOutputStream.putNextEntry(entry); + + byte[] buffer = new byte[1024]; + int bytesRead = 0; + while ((bytesRead = entryInputStream.read(buffer)) != -1) { + tempJarOutputStream.write(buffer, 0, bytesRead); + } + } + + JarEntry entryNew = new JarEntry("credentials.txt"); + InputStream entryInputStream = new FileInputStream(credFile); + tempJarOutputStream.putNextEntry(entryNew); + + byte[] buffer = new byte[1024]; + int bytesRead = 0; + while ((bytesRead = entryInputStream.read(buffer)) != -1) { + tempJarOutputStream.write(buffer, 0, bytesRead); + } + + } catch (Exception ex) { + ex.printStackTrace(); + } + + } finally { + if (jarFile != null) { + try { + jarFile.close(); + } catch (Exception ignored) { + + } + } + } + + servletResponse.setContentType("application/octet-stream"); + servletResponse.setHeader("Content-Disposition", "filename=\"Akto.jar\""); + System.out.println("set header done"); + File srcFile = new File(tmpJarFile.getPath()); + try { + FileUtils.copyFile(srcFile, servletResponse.getOutputStream()); + } catch (IOException e) { + addActionError("Failed sending jar file"); + return ERROR.toUpperCase(); + } + + System.out.println("done"); + + credFile.delete(); + tmpJarFile.delete(); + + BurpPluginInfoDao.instance.updateLastDownloadedTimestamp(getSUser().getLogin()); + + return null; + } + + private BurpPluginInfo burpPluginInfo; + public String fetchBurpPluginInfo() { + burpPluginInfo = BurpPluginInfoDao.instance.findByUsername(this.getSUser().getLogin()); + return SUCCESS.toUpperCase(); + } + + + public String version; + public int latestVersion; + public String sendHealthCheck() { + int versionInt; + try { + versionInt = Integer.parseInt(this.version); + } catch (Exception e) { + e.printStackTrace(); + versionInt = -1; + } + + BurpPluginInfoDao.instance.updateOne( + BurpPluginInfoDao.filterByUsername(this.getSUser().getLogin()), + Updates.combine( + Updates.set(BurpPluginInfo.LAST_BOOT_UP_TIMESTAMP, Context.now()), + Updates.set(BurpPluginInfo.VERSION, versionInt) + ) + ); + + latestVersion = InitializerListener.burpPluginVersion; + + return SUCCESS.toUpperCase(); + } + + protected HttpServletResponse servletResponse; + @Override + public void setServletResponse(HttpServletResponse response) { + this.servletResponse= response; + } + + protected HttpServletRequest servletRequest; + + @Override + public void setServletRequest(HttpServletRequest request) { + this.servletRequest = request; + } + + public BurpPluginInfo getBurpPluginInfo() { + return burpPluginInfo; + } + + public void setVersion(String version) { + this.version = version; + } + + public int getLatestVersion() { + return latestVersion; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/CustomAuthTypeAction.java b/apps/dashboard/src/main/java/com/akto/action/CustomAuthTypeAction.java new file mode 100644 index 0000000000..9a9046276b --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/CustomAuthTypeAction.java @@ -0,0 +1,145 @@ +package com.akto.action; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.akto.dao.CustomAuthTypeDao; +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.CustomAuthType; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.opensymphony.xwork2.Action; +import com.akto.dto.User; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.utils.CustomAuthUtil; + +public class CustomAuthTypeAction extends UserAction{ + private String name; + private List headerKeys; + private List payloadKeys; + private boolean active; + private List customAuthTypes; + private Map usersMap; + private CustomAuthType customAuthType; + + private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + + public String fetchCustomAuthTypes(){ + customAuthTypes = CustomAuthTypeDao.instance.findAll(new BasicDBObject()); + Set userIds = new HashSet<>(); + for (CustomAuthType customAuthType: customAuthTypes) { + userIds.add(customAuthType.getCreatorId()); + } + usersMap = UsersDao.instance.getUsernames(userIds); + return Action.SUCCESS.toUpperCase(); + } + + public String addCustomAuthType(){ + User user = getSUser(); + customAuthType = CustomAuthTypeDao.instance.findOne(CustomAuthType.NAME,name); + if(customAuthType!=null){ + addActionError("Auth type name needs to be unique"); + return ERROR.toUpperCase(); + } else { + active = true; + customAuthType = new CustomAuthType(name, headerKeys, payloadKeys, active,user.getId()); + CustomAuthTypeDao.instance.insertOne(customAuthType); + } + fetchCustomAuthTypes(); + SingleTypeInfo.fetchCustomAuthTypes(); + int accountId = Context.accountId.get(); + executorService.schedule( new Runnable() { + public void run() { + Context.accountId.set(accountId); + CustomAuthUtil.customAuthTypeUtil(SingleTypeInfo.activeCustomAuthTypes); + } + }, 5 , TimeUnit.SECONDS); + return Action.SUCCESS.toUpperCase(); + } + + public String updateCustomAuthType(){ + User user = getSUser(); + customAuthType = CustomAuthTypeDao.instance.findOne(CustomAuthType.NAME,name); + if(customAuthType==null){ + addActionError("Custom Auth Type does not exist"); + return ERROR.toUpperCase(); + } else if(user.getId()!=customAuthType.getCreatorId()){ + addActionError("Unautherized Request"); + return ERROR.toUpperCase(); + } else { + CustomAuthTypeDao.instance.updateOne(Filters.eq(CustomAuthType.NAME, name), + Updates.combine( + Updates.set(CustomAuthType.ACTIVE, active), + Updates.set("headerKeys", headerKeys), + Updates.set("payloadKeys", payloadKeys), + Updates.set(CustomAuthType.NAME, name), + Updates.set("timestamp", Context.now()))); + } + fetchCustomAuthTypes(); + SingleTypeInfo.fetchCustomAuthTypes(); + customAuthType = CustomAuthTypeDao.instance.findOne(CustomAuthType.NAME,name); + int accountId = Context.accountId.get(); + executorService.schedule( new Runnable() { + public void run() { + Context.accountId.set(accountId); + CustomAuthUtil.customAuthTypeUtil(SingleTypeInfo.activeCustomAuthTypes); + } + }, 5 , TimeUnit.SECONDS); + return Action.SUCCESS.toUpperCase(); + } + + public String updateCustomAuthTypeStatus(){ + User user = getSUser(); + customAuthType = CustomAuthTypeDao.instance.findOne(CustomAuthType.NAME,name); + if(customAuthType==null){ + addActionError("Custom Auth Type does not exist"); + return ERROR.toUpperCase(); + } else if(user.getId()!=customAuthType.getCreatorId()){ + addActionError("Unautherized Request"); + return ERROR.toUpperCase(); + } else { + CustomAuthTypeDao.instance.updateOne(Filters.eq(CustomAuthType.NAME, name), + Updates.combine( + Updates.set(CustomAuthType.ACTIVE, active), + Updates.set("timestamp",Context.now()))); + } + fetchCustomAuthTypes(); + SingleTypeInfo.fetchCustomAuthTypes(); + customAuthType = CustomAuthTypeDao.instance.findOne(CustomAuthType.NAME,name); + return Action.SUCCESS.toUpperCase(); + } + + public void setName(String name) { + this.name = name; + } + + public void setHeaderKeys(List headerKeys) { + this.headerKeys = headerKeys; + } + + public void setPayloadKeys(List payloadKeys) { + this.payloadKeys = payloadKeys; + } + + public void setActive(boolean active) { + this.active = active; + } + + public List getCustomAuthTypes() { + return customAuthTypes; + } + + public Map getUsersMap() { + return usersMap; + } + public CustomAuthType getCustomAuthType() { + return customAuthType; + } +} \ No newline at end of file diff --git a/apps/dashboard/src/main/java/com/akto/action/CustomDataTypeAction.java b/apps/dashboard/src/main/java/com/akto/action/CustomDataTypeAction.java new file mode 100644 index 0000000000..8c0ba003be --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/CustomDataTypeAction.java @@ -0,0 +1,584 @@ +package com.akto.action; + + +import com.akto.dao.AktoDataTypeDao; +import com.akto.dao.CustomDataTypeDao; +import com.akto.dao.SampleDataDao; +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.AktoDataType; +import com.akto.dto.CustomDataType; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.IgnoreData; +import com.akto.dto.User; +import com.akto.dto.data_types.*; +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.parsers.HttpCallParser; +import com.akto.utils.AktoCustomException; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.mongodb.BasicDBObject; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.FindOneAndUpdateOptions; +import com.mongodb.client.model.ReturnDocument; +import com.mongodb.client.model.Updates; +import com.opensymphony.xwork2.Action; + + +import java.io.IOException; +import java.util.*; + +import org.apache.commons.lang3.EnumUtils; + +public class CustomDataTypeAction extends UserAction{ + + private boolean createNew; + private String name; + private boolean sensitiveAlways; + private List sensitivePosition; + private String operator; + + private String keyOperator; + private List keyConditionFromUsers; + + private String valueOperator; + private List valueConditionFromUsers; + + public static class ConditionFromUser { + Predicate.Type type; + Map valueMap; + + public ConditionFromUser(Predicate.Type type, Map valueMap) { + this.type = type; + this.valueMap = valueMap; + } + + public ConditionFromUser() { } + + public Predicate.Type getType() { + return type; + } + + public void setType(Predicate.Type type) { + this.type = type; + } + + public Map getValueMap() { + return valueMap; + } + + public void setValueMap(Map valueMap) { + this.valueMap = valueMap; + } + } + + + private static final ObjectMapper mapper = new ObjectMapper(); + private static final JsonFactory factory = mapper.getFactory(); + + + private BasicDBObject dataTypes; + public String fetchDataTypesForSettings() { + List customDataTypes = CustomDataTypeDao.instance.findAll(new BasicDBObject()); + Collections.reverse(customDataTypes); + + Set userIds = new HashSet<>(); + for (CustomDataType customDataType: customDataTypes) { + userIds.add(customDataType.getCreatorId()); + } + userIds.add(getSUser().getId()); + + Map usersMap = UsersDao.instance.getUsernames(userIds); + + dataTypes = new BasicDBObject(); + dataTypes.put("customDataTypes", customDataTypes); + dataTypes.put("usersMap", usersMap); + List aktoDataTypes = AktoDataTypeDao.instance.findAll(new BasicDBObject()); + dataTypes.put("aktoDataTypes", aktoDataTypes); + + return Action.SUCCESS.toUpperCase(); + } + + List allDataTypes; + public String fetchDataTypeNames() { + this.allDataTypes = new ArrayList<>(); + List customDataTypes = CustomDataTypeDao.instance.findAll(new BasicDBObject()); + for (CustomDataType cdt: customDataTypes) { + allDataTypes.add(cdt.getName()); + } + for (SingleTypeInfo.SubType subType: SingleTypeInfo.subTypeMap.values()) { + allDataTypes.add(subType.getName()); + } + + return Action.SUCCESS.toUpperCase(); + } + + public BasicDBObject getDataTypes() { + return dataTypes; + } + + private CustomDataType customDataType;; + + @Override + public String execute() { + User user = getSUser(); + customDataType = null; + try { + customDataType = generateCustomDataType(user.getId()); + } catch (AktoCustomException e) { + addActionError(e.getMessage()); + return ERROR.toUpperCase(); + } + + if (this.createNew) { + CustomDataType customDataTypeFromDb = CustomDataTypeDao.instance.findOne(Filters.eq(CustomDataType.NAME, name)); + if (customDataTypeFromDb != null) { + addActionError("Data type with same name exists"); + return ERROR.toUpperCase(); + } + // id is automatically set when inserting in pojo + CustomDataTypeDao.instance.insertOne(customDataType); + } else { + FindOneAndUpdateOptions options = new FindOneAndUpdateOptions(); + options.returnDocument(ReturnDocument.AFTER); + options.upsert(false); + customDataType = CustomDataTypeDao.instance.getMCollection().findOneAndUpdate( + Filters.eq("name", customDataType.getName()), + Updates.combine( + Updates.set(CustomDataType.SENSITIVE_ALWAYS,customDataType.isSensitiveAlways()), + Updates.set(CustomDataType.SENSITIVE_POSITION,customDataType.getSensitivePosition()), + Updates.set(CustomDataType.KEY_CONDITIONS,customDataType.getKeyConditions()), + Updates.set(CustomDataType.VALUE_CONDITIONS,customDataType.getValueConditions()), + Updates.set(CustomDataType.OPERATOR,customDataType.getOperator()), + Updates.set(CustomDataType.TIMESTAMP,Context.now()), + Updates.set(CustomDataType.ACTIVE,active) + ), + options + ); + + if (customDataType == null) { + addActionError("Failed to update data type"); + return ERROR.toUpperCase(); + } + } + + SingleTypeInfo.fetchCustomDataTypes(); + + + return Action.SUCCESS.toUpperCase(); + } + + private AktoDataType aktoDataType; + + public String saveAktoDataType(){ + + aktoDataType = AktoDataTypeDao.instance.findOne("name",name); + if(aktoDataType==null){ + addActionError("invalid data type"); + return ERROR.toUpperCase(); + } + + List sensitivePositions = new ArrayList<>(); + try { + sensitivePositions = generatePositions(sensitivePosition); + } catch (Exception ignored) { + addActionError("Invalid positions for sensitive data"); + return ERROR.toUpperCase(); + } + + FindOneAndUpdateOptions options = new FindOneAndUpdateOptions(); + options.returnDocument(ReturnDocument.AFTER); + options.upsert(false); + aktoDataType = AktoDataTypeDao.instance.getMCollection().findOneAndUpdate( + Filters.eq("name", aktoDataType.getName()), + Updates.combine( + Updates.set("sensitiveAlways",sensitiveAlways), + Updates.set("sensitivePosition",sensitivePositions), + Updates.set("timestamp",Context.now()) + ), + options + ); + + if (aktoDataType == null) { + addActionError("Failed to update data type"); + return ERROR.toUpperCase(); + } + + SingleTypeInfo.fetchCustomDataTypes(); + + return Action.SUCCESS.toUpperCase(); + } + + public List generatePositions(List sensitivePosition){ + + Set sensitivePositionSet = new HashSet<>(); + if(sensitivePosition!=null && sensitivePosition.size()>0){ + for(String s:sensitivePosition){ + if(EnumUtils.isValidEnumIgnoreCase(SingleTypeInfo.Position.class, s)){ + sensitivePositionSet.add(SingleTypeInfo.Position.valueOf(s.toUpperCase())); + } + } + } + List sensitivePositions = new ArrayList(sensitivePositionSet); + return sensitivePositions; + } + + public static class CustomSubTypeMatch { + + private int apiCollectionId; + private String url, method, key, value; + + public CustomSubTypeMatch(int apiCollectionId, String url, String method, String key, String value) { + this.apiCollectionId = apiCollectionId; + this.url = url; + this.method = method; + this.key = key; + this.value = value; + } + + public CustomSubTypeMatch() { + } + + public int getApiCollectionId() { + return apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + private List customSubTypeMatches; + + + private int pageNum; + private long totalSampleDataCount; + private long currentProcessed; + private static final int pageSize = 1000; + public String reviewCustomDataType() { + customSubTypeMatches = new ArrayList<>(); + CustomDataType customDataType; + try { + customDataType = generateCustomDataType(getSUser().getId()); + } catch (AktoCustomException e) { + addActionError(e.getMessage()); + return ERROR.toUpperCase(); + } + + totalSampleDataCount = SampleDataDao.instance.getMCollection().estimatedDocumentCount(); + + MongoCursor cursor = SampleDataDao.instance.getMCollection().find().skip(pageSize*(pageNum-1)).limit(pageSize).cursor(); + currentProcessed = 0; + while(cursor.hasNext()) { + SampleData sampleData = cursor.next(); + + List samples = sampleData.getSamples(); + boolean skip = false; + for (String sample: samples) { + Key apiKey = sampleData.getId(); + try { + HttpResponseParams httpResponseParams = HttpCallParser.parseKafkaMessage(sample); + boolean skip1 = ( customDataType.isSensitiveAlways() || customDataType.getSensitivePosition().contains(SingleTypeInfo.Position.RESPONSE_HEADER) ) ? forHeaders(httpResponseParams.getHeaders(), customDataType, apiKey) : false; + boolean skip2 = ( customDataType.isSensitiveAlways() || customDataType.getSensitivePosition().contains(SingleTypeInfo.Position.REQUEST_HEADER) ) ? forHeaders(httpResponseParams.requestParams.getHeaders(), customDataType, apiKey) : false; + boolean skip3 = ( customDataType.isSensitiveAlways() || customDataType.getSensitivePosition().contains(SingleTypeInfo.Position.RESPONSE_PAYLOAD) ) ? forPayload(httpResponseParams.getPayload(), customDataType, apiKey) : false; + boolean skip4 = ( customDataType.isSensitiveAlways() || customDataType.getSensitivePosition().contains(SingleTypeInfo.Position.REQUEST_PAYLOAD) ) ? forPayload(httpResponseParams.requestParams.getPayload(), customDataType, apiKey) : false; + skip = skip1 || skip2 || skip3 || skip4; + } catch (Exception e) { + ; + } + + if (skip) break; + } + + currentProcessed += 1; + } + + return SUCCESS.toUpperCase(); + } + + public boolean forHeaders(Map> headers, CustomDataType customDataType, Key apiKey) { + boolean matchFound = false; + for (String headerName: headers.keySet()) { + List headerValues = headers.get(headerName); + for (String value: headerValues) { + boolean result = customDataType.validate(value,headerName); + if (result) { + matchFound = true; + CustomSubTypeMatch customSubTypeMatch = new CustomSubTypeMatch( + apiKey.getApiCollectionId(),apiKey.url,apiKey.method.name(),headerName, value + ); + this.customSubTypeMatches.add(customSubTypeMatch); + } + } + } + return matchFound; + } + + static class MatchResult { + String key; + Object value; + + public MatchResult(String key, Object value) { + this.key = key; + this.value = value; + } + } + + public static void extractAllValuesFromPayload(JsonNode node, String key, CustomDataType customDataType, List matches) { + if (node == null) return; + if (node.isValueNode()) { + Object value = mapper.convertValue(node, Object.class); + boolean result = customDataType.validate(value, key); + if (result) matches.add(new MatchResult(key, value)); + } else if (node.isArray()) { + ArrayNode arrayNode = (ArrayNode) node; + for(int i = 0; i < arrayNode.size(); i++) { + JsonNode arrayElement = arrayNode.get(i); + extractAllValuesFromPayload(arrayElement, null, customDataType, matches); + } + } else { + Iterator fieldNames = node.fieldNames(); + while(fieldNames.hasNext()) { + String fieldName = fieldNames.next(); + JsonNode fieldValue = node.get(fieldName); + extractAllValuesFromPayload(fieldValue, fieldName, customDataType, matches); + } + } + + } + + public boolean forPayload(String payload, CustomDataType customDataType, Key apiKey) throws IOException { + JsonParser jp = factory.createParser(payload); + JsonNode node = mapper.readTree(jp); + + List matchResults = new ArrayList<>(); + extractAllValuesFromPayload(node,null, customDataType, matchResults); + + for (MatchResult matchResult: matchResults) { + CustomSubTypeMatch customSubTypeMatch = new CustomSubTypeMatch( + apiKey.getApiCollectionId(),apiKey.url,apiKey.method.name(), matchResult.key, matchResult.value.toString() + ); + this.customSubTypeMatches.add(customSubTypeMatch); + } + + return matchResults.size() > 0; + + } + + public CustomDataType generateCustomDataType(int userId) throws AktoCustomException { + if (name == null || name.length() == 0) throw new AktoCustomException("Name cannot be empty"); + if (name.split(" ").length > 1) throw new AktoCustomException("Name has to be single word"); + if (name.length() > 15) throw new AktoCustomException("Maximum length allowed is 15 characters"); + name = name.trim(); + name = name.toUpperCase(); + if (!(name.matches("[A-Z_0-9]+"))) throw new AktoCustomException("Name can only contain alphabets, numbers and underscores"); + + if (SingleTypeInfo.subTypeMap.containsKey(name)) { + throw new AktoCustomException("Data type name reserved"); + } + + + Conditions keyConditions = null; + if (keyConditionFromUsers != null && keyOperator != null) { + + Conditions.Operator kOperator; + try { + kOperator = Conditions.Operator.valueOf(keyOperator); + } catch (Exception ignored) { + throw new AktoCustomException("Invalid key operator"); + } + + List predicates = new ArrayList<>(); + for (ConditionFromUser conditionFromUser: keyConditionFromUsers) { + Predicate predicate = Predicate.generatePredicate(conditionFromUser.type, conditionFromUser.valueMap); + if (predicate == null) { + throw new AktoCustomException("Invalid key conditions"); + } else { + predicates.add(predicate); + } + } + + if (predicates.size() > 0) { + keyConditions = new Conditions(predicates, kOperator); + } + } + + Conditions valueConditions = null; + if (valueConditionFromUsers != null && valueOperator != null) { + Conditions.Operator vOperator; + try { + vOperator = Conditions.Operator.valueOf(valueOperator); + } catch (Exception ignored) { + throw new AktoCustomException("Invalid value operator"); + } + + List predicates = new ArrayList<>(); + for (ConditionFromUser conditionFromUser: valueConditionFromUsers) { + Predicate predicate = Predicate.generatePredicate(conditionFromUser.type, conditionFromUser.valueMap); + if (predicate == null) { + throw new AktoCustomException("Invalid value conditions"); + } else { + predicates.add(predicate); + } + } + + if (predicates.size() > 0) { + valueConditions = new Conditions(predicates, vOperator); + } + } + + if ((keyConditions == null || keyConditions.getPredicates() == null || keyConditions.getPredicates().size() == 0) && + (valueConditions == null || valueConditions.getPredicates() ==null || valueConditions.getPredicates().size() == 0)) { + + throw new AktoCustomException("Both key and value conditions can't be empty"); + } + + Conditions.Operator mainOperator; + try { + mainOperator = Conditions.Operator.valueOf(operator); + } catch (Exception ignored) { + throw new AktoCustomException("Invalid value operator"); + } + + List sensitivePositions = new ArrayList<>(); + try { + sensitivePositions = generatePositions(sensitivePosition); + } catch (Exception ignored) { + throw new AktoCustomException("Invalid positions for sensitive data"); + } + + IgnoreData ignoreData = new IgnoreData(); + return new CustomDataType(name, sensitiveAlways, sensitivePositions, userId, + true,keyConditions,valueConditions, mainOperator,ignoreData); + } + + public void setCreateNew(boolean createNew) { + this.createNew = createNew; + } + + public void setName(String name) { + this.name = name; + } + + public void setSensitiveAlways(boolean sensitiveAlways) { + this.sensitiveAlways = sensitiveAlways; + } + + public void setOperator(String operator) { + this.operator = operator; + } + + public void setKeyOperator(String keyOperator) { + this.keyOperator = keyOperator; + } + + public void setKeyConditionFromUsers(List keyConditionFromUsers) { + this.keyConditionFromUsers = keyConditionFromUsers; + } + + public void setValueOperator(String valueOperator) { + this.valueOperator = valueOperator; + } + + public void setValueConditionFromUsers(List valueConditionFromUsers) { + this.valueConditionFromUsers = valueConditionFromUsers; + } + + public List getCustomSubTypeMatches() { + return customSubTypeMatches; + } + + public CustomDataType getCustomDataType() { + return customDataType; + } + + public AktoDataType getAktoDataType() { + return aktoDataType; + } + + private boolean active; + public String toggleDataTypeActiveParam() { + FindOneAndUpdateOptions options = new FindOneAndUpdateOptions(); + options.returnDocument(ReturnDocument.AFTER); + options.upsert(false); + customDataType = CustomDataTypeDao.instance.getMCollection().findOneAndUpdate( + Filters.eq(CustomDataType.NAME, this.name), + Updates.set(CustomDataType.ACTIVE, active), + options + ); + + if (customDataType == null) { + String v = active ? "activate" : "deactivate"; + addActionError("Failed to "+ v +" data type"); + return ERROR.toUpperCase(); + } + + return Action.SUCCESS.toUpperCase(); + } + + public void setActive(boolean active) { + this.active = active; + } + + public void setPageNum(int pageNum) { + this.pageNum = pageNum; + } + + public long getTotalSampleDataCount() { + return totalSampleDataCount; + } + + public long getCurrentProcessed() { + return currentProcessed; + } + + public List getAllDataTypes() { + return allDataTypes; + } + + public List getSensitivePosition() { + return sensitivePosition; + } + + public void setSensitivePosition(List sensitivePosition) { + this.sensitivePosition = sensitivePosition; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/ExportSampleDataAction.java b/apps/dashboard/src/main/java/com/akto/action/ExportSampleDataAction.java new file mode 100644 index 0000000000..0d51389682 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/ExportSampleDataAction.java @@ -0,0 +1,312 @@ +package com.akto.action; + +import com.akto.DaoInit; +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.SampleDataDao; +import com.akto.dao.context.Context; +import com.akto.dto.*; +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.URLMethods; +import com.akto.parsers.HttpCallParser; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; + +import java.io.IOException; +import java.util.*; + +public class ExportSampleDataAction extends UserAction { + private final static ObjectMapper mapper = new ObjectMapper(); + JsonFactory factory = mapper.getFactory(); + @Override + public String execute() { + return SUCCESS.toUpperCase(); + } + + private String collectionName; + private String lastUrlFetched; + private String lastMethodFetched; + private int limit; + private final List importInBurpResult = new ArrayList<>(); + public String importInBurp() { + if (limit <= 0 || limit > 500 ) limit = 500; + ApiCollection apiCollection = ApiCollectionsDao.instance.findByName(collectionName); + + if (apiCollection == null) { + addActionError("Invalid collection"); + return ERROR.toUpperCase(); + } + + int apiCollectionId = apiCollection.getId(); + + List sampleDataList = SampleDataDao.instance.fetchSampleDataPaginated(apiCollectionId, lastUrlFetched, lastMethodFetched, limit); + + lastMethodFetched = null; + lastUrlFetched = null; + + for (SampleData s: sampleDataList) { + List samples = s.getSamples(); + if (samples.size() < 1) continue; + + String msg = samples.get(0); + Map burpRequestFromSampleData = generateBurpRequestFromSampleData(msg); + // use url from the sample data instead of relying on the id + // this is to handle parameterised URLs + String url = burpRequestFromSampleData.get("url"); + String req = burpRequestFromSampleData.get("request"); + String httpType = burpRequestFromSampleData.get("type"); + String resp = generateBurpResponseFromSampleData(msg, httpType); + + BasicDBObject res = new BasicDBObject(); + res.put("url", url); + res.put("req", req); + res.put("res", resp); + + importInBurpResult.add(res); + + // But for lastUrlFetched we need the id because mongo query uses the one in _id + lastUrlFetched = s.getId().url; + lastMethodFetched = s.getId().method.name(); + } + + return SUCCESS.toUpperCase(); + } + + private String burpRequest; + public String generateBurpRequest() { + if (sampleData == null) { + addActionError("Invalid sample data"); + return ERROR.toUpperCase(); + } + + Map result = generateBurpRequestFromSampleData(sampleData); + burpRequest = result.get("request"); + return SUCCESS.toUpperCase(); + } + + + private Map generateBurpRequestFromSampleData(String sampleData) { + OriginalHttpRequest originalHttpRequest = new OriginalHttpRequest(); + try { + originalHttpRequest.buildFromSampleMessage(sampleData); + } catch (Exception e) { + originalHttpRequest.buildFromApiSampleMessage(sampleData); + } + + StringBuilder builder = new StringBuilder(""); + + String url = originalHttpRequest.getFullUrlWithParams(); + + if (!url.startsWith("http")) { + String host = originalHttpRequest.findHostFromHeader(); + String protocol = originalHttpRequest.findProtocolFromHeader(); + try { + url = OriginalHttpRequest.makeUrlAbsolute(url,host, protocol); + } catch (Exception e) { + + } + } + + // METHOD and PATH + builder.append(originalHttpRequest.getMethod()).append(" ") + .append(url).append(" ") + .append(originalHttpRequest.getType()) + .append("\n"); + + // HEADERS + addHeadersBurp(originalHttpRequest.getHeaders(), builder); + + builder.append("\n"); + + // BODY + builder.append(originalHttpRequest.getBody()); + + Map result = new HashMap<>(); + result.put("request", builder.toString()); + result.put("url", url); + result.put("type", originalHttpRequest.getType()); + + return result; + } + + private String generateBurpResponseFromSampleData(String sampleData, String httpType) { + OriginalHttpResponse originalHttpResponse = new OriginalHttpResponse(); + originalHttpResponse.buildFromSampleMessage(sampleData); + + OriginalHttpRequest originalHttpRequest = new OriginalHttpRequest(); + originalHttpRequest.buildFromSampleMessage(sampleData); + + StringBuilder builder = new StringBuilder(""); + + // HTTP type + builder.append(httpType).append(" ").append(originalHttpResponse.getStatusCode()).append(" ").append("\n"); + + // Headers + addHeadersBurp(originalHttpResponse.getHeaders(), builder); + + builder.append("\n"); + + // Body + builder.append(originalHttpResponse.getBody()); + + return builder.toString(); + } + + private void addHeadersBurp(Map> headers, StringBuilder builder) { + for (String headerName: headers.keySet()) { + List values = headers.get(headerName); + if (values == null || values.isEmpty() || headerName.length()<1) continue; + String prettyHeaderName = headerName.substring(0, 1).toUpperCase() + headerName.substring(1); + String value = String.join(",", values); + builder.append(prettyHeaderName).append(": ").append(value); + builder.append("\n"); + } + } + + private String curlString; + private String sampleData; + public String generateCurl() { + HttpResponseParams httpResponseParams; + try { + httpResponseParams = HttpCallParser.parseKafkaMessage(sampleData); + } catch (Exception e) { + try { + OriginalHttpRequest originalHttpRequest = new OriginalHttpRequest(); + originalHttpRequest.buildFromApiSampleMessage(sampleData); + + HttpRequestParams httpRequestParams = new HttpRequestParams( + originalHttpRequest.getMethod(), originalHttpRequest.getUrl(), originalHttpRequest.getType(), + originalHttpRequest.getHeaders(), originalHttpRequest.getBody(), 0 + ); + + httpResponseParams = new HttpResponseParams(); + httpResponseParams.requestParams = httpRequestParams; + } catch (Exception e1) { + addActionError("Couldn't parse the data"); + return ERROR.toUpperCase(); + } + + } + + HttpRequestParams httpRequestParams = httpResponseParams.getRequestParams(); + + StringBuilder builder = new StringBuilder("curl -v "); + + // Method + builder.append("-X ").append(httpRequestParams.getMethod()).append(" \\\n "); + + String hostName = null; + // Headers + for (Map.Entry> entry : httpRequestParams.getHeaders().entrySet()) { + if (entry.getKey().equalsIgnoreCase("host") && entry.getValue().size() > 0) { + hostName = entry.getValue().get(0); + } + builder.append("-H '").append(entry.getKey()).append(":"); + for (String value : entry.getValue()) { + builder.append(" ").append(value.replaceAll("\"", "\\\\\"")); + } + builder.append("' \\\n "); + } + + String urlString; + String path = httpRequestParams.getURL(); + if (hostName != null && !(path.toLowerCase().startsWith("http") || path.toLowerCase().startsWith("www."))) { + urlString = path.startsWith("/") ? hostName + path : hostName + "/" + path; + } else { + urlString = path; + } + + StringBuilder url = new StringBuilder(urlString); + + // Body + try { + String payload = httpRequestParams.getPayload(); + if (payload == null) payload = ""; + boolean curlyBracesCond = payload.startsWith("{") && payload.endsWith("}"); + boolean squareBracesCond = payload.startsWith("[") && payload.endsWith("]"); + if (curlyBracesCond || squareBracesCond) { + if (!Objects.equals(httpRequestParams.getMethod(), "GET")) { + builder.append("-d '").append(payload).append("' \\\n "); + } else { + JsonParser jp = factory.createParser(payload); + JsonNode node = mapper.readTree(jp); + if (node != null) { + Iterator fieldNames = node.fieldNames(); + boolean flag =true; + while(fieldNames.hasNext()) { + String fieldName = fieldNames.next(); + JsonNode fieldValue = node.get(fieldName); + if (fieldValue.isValueNode()) { + if (flag) { + url.append("?").append(fieldName).append("=").append(fieldValue.asText()); + flag = false; + } else { + url.append("&").append(fieldName).append("=").append(fieldValue.asText()); + } + } + } + } + } + } + } catch (Exception e) { + ; + addActionError("Error parsing the body"); + return ERROR.toUpperCase(); + } + + + // URL + builder.append("\"").append(url).append("\""); + + curlString = builder.toString(); + + return SUCCESS.toUpperCase(); + } + + public String getCurlString() { + return curlString; + } + + public void setSampleData(String sampleData) { + this.sampleData = sampleData; + } + + public String getBurpRequest() { + return burpRequest; + } + + public void setCollectionName(String collectionName) { + this.collectionName = collectionName; + } + + public List getImportInBurpResult() { + return importInBurpResult; + } + + public void setLastUrlFetched(String lastUrlFetched) { + this.lastUrlFetched = lastUrlFetched; + } + + public void setLastMethodFetched(String lastMethodFetched) { + this.lastMethodFetched = lastMethodFetched; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public String getLastUrlFetched() { + return lastUrlFetched; + } + + public String getLastMethodFetched() { + return lastMethodFetched; + } +} + diff --git a/apps/dashboard/src/main/java/com/akto/action/FilterAction.java b/apps/dashboard/src/main/java/com/akto/action/FilterAction.java new file mode 100644 index 0000000000..8b04231367 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/FilterAction.java @@ -0,0 +1,21 @@ +package com.akto.action; + +import com.akto.dao.RuntimeFilterDao; +import com.akto.dto.runtime_filters.RuntimeFilter; +import com.mongodb.BasicDBObject; + +import java.util.*; + +public class FilterAction extends UserAction{ + + private List runtimeFilters; + @Override + public String execute(){ + runtimeFilters = RuntimeFilterDao.instance.findAll(new BasicDBObject()); + return SUCCESS.toUpperCase(); + } + + public List getRuntimeFilters() { + return runtimeFilters; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/HarAction.java b/apps/dashboard/src/main/java/com/akto/action/HarAction.java new file mode 100644 index 0000000000..4ffe30c8f3 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/HarAction.java @@ -0,0 +1,196 @@ +package com.akto.action; + +import com.akto.DaoInit; +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.BurpPluginInfoDao; +import com.akto.dao.RuntimeFilterDao; +import com.akto.dao.context.Context; +import com.akto.dto.ApiCollection; +import com.akto.har.HAR; +import com.akto.listener.KafkaListener; +import com.akto.parsers.HttpCallParser; +import com.akto.runtime.APICatalogSync; +import com.akto.runtime.policies.AktoPolicy; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.ApiToken.Utility; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.utils.DashboardMode; +import com.akto.utils.Utils; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import com.opensymphony.xwork2.Action; +import com.sun.jna.*; + +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class HarAction extends UserAction { + private String harString; + private List harErrors; + private BasicDBObject content; + private int apiCollectionId; + private String apiCollectionName; + + private boolean skipKafka = DashboardMode.isLocalDeployment(); + private byte[] tcpContent; + + @Override + public String execute() throws IOException { + ApiCollection apiCollection = null; + if (apiCollectionName != null) { + apiCollection = ApiCollectionsDao.instance.findByName(apiCollectionName); + if (apiCollection == null) { + ApiCollectionsAction apiCollectionsAction = new ApiCollectionsAction(); + apiCollectionsAction.setSession(this.getSession()); + apiCollectionsAction.setCollectionName(apiCollectionName); + String result = apiCollectionsAction.createCollection(); + if (result.equalsIgnoreCase(Action.SUCCESS)) { + List apiCollections = apiCollectionsAction.getApiCollections(); + if (apiCollections != null && apiCollections.size() > 0) { + apiCollection = apiCollections.get(0); + } else { + addActionError("Couldn't create api collection " + apiCollectionName); + return ERROR.toUpperCase(); + } + } else { + Collection actionErrors = apiCollectionsAction.getActionErrors(); + if (actionErrors != null && actionErrors.size() > 0) { + for (String actionError: actionErrors) { + addActionError(actionError); + } + } + return ERROR.toUpperCase(); + } + } + + apiCollectionId = apiCollection.getId(); + } else { + apiCollection = ApiCollectionsDao.instance.findOne(Filters.eq("_id", apiCollectionId)); + } + + if (apiCollection == null) { + addActionError("Invalid collection name"); + return ERROR.toUpperCase(); + } + + if (apiCollection.getHostName() != null) { + addActionError("Traffic mirroring collection can't be used"); + return ERROR.toUpperCase(); + } + + if (KafkaListener.kafka == null) { + addActionError("Dashboard kafka not running"); + return ERROR.toUpperCase(); + } + + if (harString == null) { + harString = this.content.toString(); + } + String topic = System.getenv("AKTO_KAFKA_TOPIC_NAME"); + if (topic == null) topic = "akto.api.logs"; + if (harString == null) { + addActionError("Empty content"); + return ERROR.toUpperCase(); + } + + if (getSession().getOrDefault("utility","").equals(Utility.BURP.toString())) { + BurpPluginInfoDao.instance.updateLastDataSentTimestamp(getSUser().getLogin()); + } + + try { + HAR har = new HAR(); + List messages = har.getMessages(harString, apiCollectionId); + harErrors = har.getErrors(); + Utils.pushDataToKafka(apiCollectionId, topic, messages, harErrors, skipKafka); + } catch (Exception e) { + ; + return SUCCESS.toUpperCase(); + } + return SUCCESS.toUpperCase(); + } + + public void setContent(BasicDBObject content) { + this.content = content; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public void setHarString(String harString) { + this.harString = harString; + } + + public void setApiCollectionName(String apiCollectionName) { + this.apiCollectionName = apiCollectionName; + } + + public List getHarErrors() { + return harErrors; + } + + public boolean getSkipKafka() { + return this.skipKafka; + } + + public void setTcpContent(byte[] tcpContent) { + this.tcpContent = tcpContent; + } + + Awesome awesome = null; + + public String uploadTcp() { + + File tmpDir = FileUtils.getTempDirectory(); + String filename = UUID.randomUUID().toString() + ".pcap"; + File tcpDump = new File(tmpDir, filename); + try { + FileUtils.writeByteArrayToFile(tcpDump, tcpContent); + Awesome awesome = (Awesome) Native.load("awesome", Awesome.class); + Awesome.GoString.ByValue str = new Awesome.GoString.ByValue(); + str.p = tcpDump.getAbsolutePath(); + str.n = str.p.length(); + + Awesome.GoString.ByValue str2 = new Awesome.GoString.ByValue(); + str2.p = System.getenv("AKTO_KAFKA_BROKER_URL"); + str2.n = str2.p.length(); + + awesome.readTcpDumpFile(str, str2 , apiCollectionId); + + return Action.SUCCESS.toUpperCase(); + } catch (IOException e) { + ; + return Action.ERROR.toUpperCase(); + } + + } + + interface Awesome extends Library { + public static class GoString extends Structure { + /** C type : const char* */ + public String p; + public long n; + public GoString() { + super(); + } + protected List getFieldOrder() { + return Arrays.asList("p", "n"); + } + /** @param p C type : const char* */ + public GoString(String p, long n) { + super(); + this.p = p; + this.n = n; + } + public static class ByReference extends GoString implements Structure.ByReference {} + public static class ByValue extends GoString implements Structure.ByValue {} + } + + public void readTcpDumpFile(GoString.ByValue filepath, GoString.ByValue kafkaURL, long apiCollectionId); + + } +} \ No newline at end of file diff --git a/apps/dashboard/src/main/java/com/akto/action/HomeAction.java b/apps/dashboard/src/main/java/com/akto/action/HomeAction.java new file mode 100644 index 0000000000..1140152d85 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/HomeAction.java @@ -0,0 +1,49 @@ +package com.akto.action; + +import com.opensymphony.xwork2.Action; +import org.apache.struts2.interceptor.SessionAware; +import java.util.Map; + +// This is the first action that is triggered when the webpage is first fetched +// Basically sets the access token from the session (saved by UserDetailsFilter) +// Then the accessToken is accessed by login.jsp (the page being requested) +// in ${accessToken} field. +public class HomeAction implements Action, SessionAware { + @Override + public String execute() { + return "SUCCESS"; + } + + private String accessToken; + private String signupInvitationCode; + private String signupEmailId; + + // to prevent redirect_uri not found warning + public void setRedirect_uri(String redirect_uri) { + } + + public String getAccessToken() { + return accessToken; + } + + @Override + public void setSession(Map session) { + this.accessToken = (String) session.get(AccessTokenAction.ACCESS_TOKEN_HEADER_NAME); + } + + public void setSignupInvitationCode(String signupInvitationCode) { + this.signupInvitationCode = signupInvitationCode; + } + + public String getSignupInvitationCode() { + return signupInvitationCode; + } + + public String getSignupEmailId() { + return signupEmailId; + } + + public void setSignupEmailId(String signupEmailId) { + this.signupEmailId = signupEmailId; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/IgnoreFalsePositivesAction.java b/apps/dashboard/src/main/java/com/akto/action/IgnoreFalsePositivesAction.java new file mode 100644 index 0000000000..3255b2dc69 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/IgnoreFalsePositivesAction.java @@ -0,0 +1,105 @@ +package com.akto.action; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.akto.dao.AktoDataTypeDao; +import com.akto.dao.CustomDataTypeDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.context.Context; +import com.akto.dto.CustomDataType; +import com.akto.dto.IgnoreData; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.SingleTypeInfo.ParamId; +import com.akto.dto.type.SingleTypeInfo.SubType; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.opensymphony.xwork2.Action; + +public class IgnoreFalsePositivesAction extends UserAction{ + private Map falsePositives; + + private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + + private SubType createSubType(String keyType){ + if(keyType.equals(SingleTypeInfo.CREDIT_CARD.getName()) || keyType.equals(SingleTypeInfo.PHONE_NUMBER.getName())){ + return SingleTypeInfo.INTEGER_64; + } + return SingleTypeInfo.GENERIC; + } + + public String setFalsePositivesInSensitiveData() { + if(falsePositives==null){ + return Action.ERROR.toUpperCase(); + } + for (String keyType : falsePositives.keySet()) { + if (SingleTypeInfo.customDataTypeMap.containsKey(keyType)) { + IgnoreData ignoreData = SingleTypeInfo.customDataTypeMap.get(keyType).getIgnoreData(); + if (ignoreData == null) { + ignoreData = new IgnoreData(new HashMap<>(), new HashSet<>()); + } + ignoreData.getIgnoredKeysInAllAPIs().addAll(falsePositives.get(keyType).getIgnoredKeysInAllAPIs()); + ignoreData.getIgnoredKeysInSelectedAPIs().putAll(falsePositives.get(keyType).getIgnoredKeysInSelectedAPIs()); + CustomDataTypeDao.instance.updateOne(Filters.eq(CustomDataType.NAME, keyType), + Updates.combine( + Updates.set(CustomDataType.IGNORE_DATA, ignoreData), + Updates.set(CustomDataType.TIMESTAMP, Context.now()))); + } else if (SingleTypeInfo.aktoDataTypeMap.containsKey(keyType)) { + IgnoreData ignoreData = SingleTypeInfo.aktoDataTypeMap.get(keyType).getIgnoreData(); + if (ignoreData == null) { + ignoreData = new IgnoreData(new HashMap<>(), new HashSet<>()); + } + ignoreData.getIgnoredKeysInAllAPIs().addAll(falsePositives.get(keyType).getIgnoredKeysInAllAPIs()); + ignoreData.getIgnoredKeysInSelectedAPIs().putAll(falsePositives.get(keyType).getIgnoredKeysInSelectedAPIs()); + AktoDataTypeDao.instance.updateOne(Filters.eq(CustomDataType.NAME, keyType), + Updates.combine( + Updates.set(CustomDataType.IGNORE_DATA, ignoreData), + Updates.set(CustomDataType.TIMESTAMP, Context.now()))); + } + } + + int accountId = Context.accountId.get(); + executorService.schedule( new Runnable() { + public void run() { + Context.accountId.set(accountId); + for (String keyType : falsePositives.keySet()) { + IgnoreData ignoreData = falsePositives.get(keyType); + for (String key : ignoreData.getIgnoredKeysInSelectedAPIs().keySet()) { + for (ParamId paramId : ignoreData.getIgnoredKeysInSelectedAPIs().get(key)) { + SingleTypeInfo sti = new SingleTypeInfo(paramId, null, null, accountId, accountId, + accountId, null, null, accountId, accountId); + sti.setSubType(createSubType(keyType)); + SingleTypeInfoDao.instance.getMCollection().deleteMany( + SingleTypeInfoDao.createFilters(sti)); + SingleTypeInfoDao.instance.updateMany( + SingleTypeInfoDao.createFiltersWithoutSubType(sti), + Updates.set(SingleTypeInfo.SUB_TYPE,createSubType(keyType).getName())); + } + } + for (String key : ignoreData.getIgnoredKeysInAllAPIs()) { + SingleTypeInfoDao.instance.getMCollection().deleteMany( + Filters.and(Filters.eq(SingleTypeInfo._PARAM, key), + Filters.eq(SingleTypeInfo.SUB_TYPE,createSubType(keyType).getName()))); + SingleTypeInfoDao.instance.updateMany( + Filters.eq(SingleTypeInfo._PARAM, key), + Updates.set(SingleTypeInfo.SUB_TYPE, createSubType(keyType).getName())); + } + } + } + }, 1 , TimeUnit.SECONDS); + + return Action.SUCCESS.toUpperCase(); + } + + public Map getFalsePositives() { + return falsePositives; + } + + public void setFalsePositives(Map falsePositives) { + this.falsePositives = falsePositives; + } +} \ No newline at end of file diff --git a/apps/dashboard/src/main/java/com/akto/action/InfraMetricsAction.java b/apps/dashboard/src/main/java/com/akto/action/InfraMetricsAction.java new file mode 100644 index 0000000000..ef299a199e --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/InfraMetricsAction.java @@ -0,0 +1,75 @@ +package com.akto.action; + + +import com.akto.dao.KafkaHealthMetricsDao; +import com.akto.dao.UsersDao; +import com.akto.dto.KafkaHealthMetric; +import com.akto.listener.InfraMetricsListener; +import com.mongodb.BasicDBObject; +import com.opensymphony.xwork2.Action; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.struts2.interceptor.ServletRequestAware; +import org.apache.struts2.interceptor.ServletResponseAware; +import org.bson.Document; + +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class InfraMetricsAction implements Action,ServletResponseAware, ServletRequestAware { + + private static final Logger logger = LoggerFactory.getLogger(InfraMetricsAction.class); + @Override + public String execute() throws Exception { + InfraMetricsListener.registry.scrape(servletResponse.getWriter()); + return null; + } + + private final BasicDBObject akto_health = new BasicDBObject(); + public String health() { + try { + Object mongoHealth = mongoHealth(); + akto_health.put("mongo", mongoHealth); + } catch (Exception e) { + akto_health.put("mongo", "Error getting health metrics from mongo. Check logs."); + logger.error("ERROR health metrics from mongo " + e); + } + + try { + List kafkaHealthMetrics = runtimeHealth(); + akto_health.put("runtime", kafkaHealthMetrics); + } catch (Exception e) { + akto_health.put("runtime", "Error getting health metrics from runtime. Check logs."); + logger.error("ERROR health metrics from runtime " + e); + } + return SUCCESS.toUpperCase(); + } + + public Object mongoHealth() { + Document stats = UsersDao.instance.getStats(); + Document metrics = (Document) stats.get("metrics"); + return metrics.get("document"); + } + + public List runtimeHealth() { + return KafkaHealthMetricsDao.instance.findAll(new BasicDBObject()); + } + + protected HttpServletResponse servletResponse; + @Override + public void setServletResponse(HttpServletResponse httpServletResponse) { + this.servletResponse= httpServletResponse; + } + + protected HttpServletRequest servletRequest; + @Override + public void setServletRequest(HttpServletRequest httpServletRequest) { + this.servletRequest = httpServletRequest; + } + + public BasicDBObject getAkto_health() { + return akto_health; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/LoginAction.java b/apps/dashboard/src/main/java/com/akto/action/LoginAction.java new file mode 100644 index 0000000000..332f29e0c6 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/LoginAction.java @@ -0,0 +1,185 @@ +package com.akto.action; + +import com.akto.dao.SignupDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.Config; +import com.akto.dto.SignupInfo; +import com.akto.dto.SignupUserInfo; +import com.akto.dto.User; +import com.akto.utils.Token; +import com.akto.utils.HttpUtils; +import com.akto.utils.JWT; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.opensymphony.xwork2.Action; +import org.apache.struts2.interceptor.ServletRequestAware; +import org.apache.struts2.interceptor.ServletResponseAware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.*; + +import static com.akto.filter.UserDetailsFilter.LOGIN_URI; + +// Validates user from the supplied username and password +// Generates refresh token jwt using the username if valid user +// Saves the refresh token to db +// Generates access token jwt using the refresh token +// Adds the refresh token to http-only cookie +// Adds the access token to header +public class LoginAction implements Action, ServletResponseAware, ServletRequestAware { + private static final Logger logger = LoggerFactory.getLogger(LoginAction.class); + + public static final String REFRESH_TOKEN_COOKIE_NAME = "refreshToken"; + public BasicDBObject getLoginResult() { + return loginResult; + } + + public void setLoginResult(BasicDBObject loginResult) { + this.loginResult = loginResult; + } + + BasicDBObject loginResult = new BasicDBObject(); + @Override + public String execute() throws IOException { + logger.info("LoginAction Hit"); + + if (username == null) { + return Action.ERROR.toUpperCase(); + } + + User user = UsersDao.instance.findOne(Filters.eq("login", username)); + + if (user != null) { + SignupInfo.PasswordHashInfo signupInfo = (SignupInfo.PasswordHashInfo) user.getSignupInfoMap().get(Config.ConfigType.PASSWORD + "-ankush"); + String salt = signupInfo.getSalt(); + String passHash = Integer.toString((salt + password).hashCode()); + if (!passHash.equals(signupInfo.getPasshash())) { + return Action.ERROR.toUpperCase(); + } + + } else { + + SignupUserInfo signupUserInfo = SignupDao.instance.findOne("user.login", username); + + if (signupUserInfo != null) { + SignupInfo.PasswordHashInfo passInfo = + (SignupInfo.PasswordHashInfo) signupUserInfo.getUser().getSignupInfoMap().get(Config.ConfigType.PASSWORD + "-ankush"); + + String passHash = Integer.toString((passInfo.getSalt() + password).hashCode()); + + if (passHash.equals(passInfo.getPasshash())) { + loginUser(signupUserInfo.getUser(), servletResponse, false, servletRequest); + loginResult.put("redirect", "/dashboard/quick-start"); + return "SUCCESS"; + } + } + + logger.info("Auth Failed"); + return "ERROR"; + } + String result = loginUser(user, servletResponse, true, servletRequest); + decideFirstPage(loginResult); + return result; + } + + private void decideFirstPage(BasicDBObject loginResult){ + Context.accountId.set(1_000_000); + long count = SingleTypeInfoDao.instance.getEstimatedCount(); + if(count == 0){ + logger.info("New user, showing quick start page"); + loginResult.put("redirect", "dashboard/quick-start"); + } else { + logger.info("Existing user, not redirecting to quick start page"); + } + } + + public static String loginUser(User user, HttpServletResponse servletResponse, boolean signedUp, HttpServletRequest servletRequest) { + String refreshToken; + Map claims = new HashMap<>(); + claims.put("username",user.getLogin()); + claims.put("signedUp",signedUp+""); + try { + refreshToken = JWT.createJWT( + "/home/avneesh/Desktop/akto/dashboard/private.pem", + claims, + "Akto", + "refreshToken", + Calendar.DAY_OF_MONTH, + 6 + ); + + List refreshTokens = user.getRefreshTokens(); + if (refreshTokens == null) { + refreshTokens = new ArrayList<>(); + } + if (refreshTokens.size() > 10) { + refreshTokens = refreshTokens.subList(refreshTokens.size()-10, refreshTokens.size()); + } + refreshTokens.add(refreshToken); + + Token token = new Token(refreshToken); + servletResponse.addHeader(AccessTokenAction.ACCESS_TOKEN_HEADER_NAME,token.getAccessToken()); + Cookie cookie = new Cookie(REFRESH_TOKEN_COOKIE_NAME, refreshToken); + cookie.setHttpOnly(true); + cookie.setPath("/dashboard"); + + cookie.setSecure(HttpUtils.isHttpsEnabled()); + + + servletResponse.addCookie(cookie); + HttpSession session = servletRequest.getSession(true); + session.setAttribute("username", user.getLogin()); + session.setAttribute("user", user); + session.setAttribute("login", Context.now()); + if (signedUp) { + UsersDao.instance.getMCollection().findOneAndUpdate( + Filters.eq("_id", user.getId()), + Updates.combine( + Updates.set("refreshTokens", refreshTokens), + Updates.set(User.LAST_LOGIN_TS, Context.now()) + ) + ); + } + return Action.SUCCESS.toUpperCase(); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException e) { + ; + } + + return Action.ERROR.toUpperCase(); + + } + + private String username; + private String password; + + + public void setUsername(String username) { + this.username = username; + } + public void setPassword(String password) { + this.password = password; + } + + protected HttpServletResponse servletResponse; + @Override + public void setServletResponse(HttpServletResponse httpServletResponse) { + this.servletResponse= httpServletResponse; + } + + protected HttpServletRequest servletRequest; + @Override + public void setServletRequest(HttpServletRequest httpServletRequest) { + this.servletRequest = httpServletRequest; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/LogoutAction.java b/apps/dashboard/src/main/java/com/akto/action/LogoutAction.java new file mode 100644 index 0000000000..2c3438e514 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/LogoutAction.java @@ -0,0 +1,55 @@ +package com.akto.action; + +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.User; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.opensymphony.xwork2.Action; +import org.apache.struts2.interceptor.ServletRequestAware; +import org.apache.struts2.interceptor.ServletResponseAware; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.util.ArrayList; + +import static com.akto.filter.UserDetailsFilter.LOGIN_URI; + +public class LogoutAction extends UserAction implements ServletRequestAware,ServletResponseAware { + @Override + public String execute() throws Exception { + User user = getSUser(); + UsersDao.instance.updateOne( + Filters.eq("_id", user.getId()), + Updates.set("refreshTokens", new ArrayList<>()) + ); + Cookie cookie = AccessTokenAction.generateDeleteCookie(); + servletResponse.addCookie(cookie); + HttpSession session = servletRequest.getSession(); + if (session != null) { + session.setAttribute("logout", Context.now()); + } + try { + servletResponse.sendRedirect(LOGIN_URI); + return null; + } catch (IOException e) { + ; + } + return Action.SUCCESS.toUpperCase(); + } + + protected HttpServletResponse servletResponse; + protected HttpServletRequest servletRequest; + @Override + public void setServletResponse(HttpServletResponse response) { + this.servletResponse= response; + } + + @Override + public void setServletRequest(HttpServletRequest request) { + this.servletRequest = request; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/LogsAction.java b/apps/dashboard/src/main/java/com/akto/action/LogsAction.java new file mode 100644 index 0000000000..970415f669 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/LogsAction.java @@ -0,0 +1,126 @@ +package com.akto.action; + +import com.amazonaws.services.logs.AWSLogs; +import com.amazonaws.services.logs.AWSLogsClientBuilder; +import com.amazonaws.services.logs.model.FilterLogEventsRequest; +import com.amazonaws.services.logs.model.FilterLogEventsResult; +import com.amazonaws.services.logs.model.FilteredLogEvent; + + +public class LogsAction extends UserAction { + + private String logGroupName; + private long startTime; + private long endTime; + private String filterPattern; + private String nextToken; + private int limit; + private String output; + + public String fetchLogs() { + + if (!logGroupName.startsWith("akto-")) { + return ERROR.toUpperCase(); + } + + this.output = ""; + + try { + AWSLogs awsLogs = AWSLogsClientBuilder.defaultClient(); + + FilterLogEventsRequest filterLogEventsRequest = + new FilterLogEventsRequest() + .withLogGroupName(logGroupName) + .withLogStreamNames(logGroupName); + + if (endTime != 0) { + filterLogEventsRequest.setEndTime(endTime); + } + + if (startTime != 0) { + filterLogEventsRequest.setStartTime(startTime); + } + + if (limit != 0) { + filterLogEventsRequest.setLimit(limit); + } + + if (filterPattern != null) { + filterLogEventsRequest.setFilterPattern(filterPattern); + } + + int counter = 0; + + do { + filterLogEventsRequest.setNextToken(nextToken); + FilterLogEventsResult filterLogEventsResult = awsLogs.filterLogEvents(filterLogEventsRequest); + for(FilteredLogEvent filteredLogEvent: filterLogEventsResult.getEvents()) { + this.output += filteredLogEvent.getTimestamp() + ": " + filteredLogEvent.getMessage() + "\n"; + counter++; + } + this.nextToken = filterLogEventsResult.getNextToken(); + } while (nextToken != null && counter < limit); + } catch (Exception e) { + this.output += e.getMessage() + "\n"; + } + + return SUCCESS.toUpperCase(); + } + + + public String getLogGroupName() { + return this.logGroupName; + } + + public void setLogGroupName(String logGroupName) { + this.logGroupName = logGroupName; + } + + public long getStartTime() { + return this.startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public long getEndTime() { + return this.endTime; + } + + public void setEndTime(long endTime) { + this.endTime = endTime; + } + + public String getFilterPattern() { + return this.filterPattern; + } + + public void setFilterPattern(String filterPattern) { + this.filterPattern = filterPattern; + } + + public String getNextToken() { + return this.nextToken; + } + + public void setNextToken(String nextToken) { + this.nextToken = nextToken; + } + + public int getLimit() { + return this.limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public String getOutput() { + return this.output; + } + + public void setOutput(String output) { + this.output = output; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/MiddlewareConfigAction.java b/apps/dashboard/src/main/java/com/akto/action/MiddlewareConfigAction.java new file mode 100644 index 0000000000..a82cd5fe63 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/MiddlewareConfigAction.java @@ -0,0 +1,31 @@ +package com.akto.action; + +import java.util.List; +import java.util.ArrayList; + +import com.mongodb.BasicDBObject; +import com.opensymphony.xwork2.Action; + +public class MiddlewareConfigAction extends UserAction { + + private String source; + private BasicDBObject response = new BasicDBObject(); + public String getMiddlewareConfig() { + + List blackList = new ArrayList<>(); + List whiteList = new ArrayList<>(); + response.put("blackList", blackList); + response.put("whiteList", whiteList); + + return Action.SUCCESS.toUpperCase(); + } + + public BasicDBObject getResponse() { + return this.response; + } + + public void setSource(String source) { + this.source = source; + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/action/OpenApiAction.java b/apps/dashboard/src/main/java/com/akto/action/OpenApiAction.java new file mode 100644 index 0000000000..e674a82363 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/OpenApiAction.java @@ -0,0 +1,85 @@ +package com.akto.action; + +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.SampleDataDao; +import com.akto.dto.ApiCollection; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.open_api.Main; +import com.akto.utils.SampleDataToSTI; +import com.mongodb.client.model.Filters; +import io.swagger.v3.oas.models.OpenAPI; +import org.apache.struts2.interceptor.ServletResponseAware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; + +public class OpenApiAction extends UserAction implements ServletResponseAware { + + private static final Logger logger = LoggerFactory.getLogger(OpenApiAction.class); + private int apiCollectionId; + private String openAPIString = null; + private boolean includeHeaders = true; + @Override + public String execute() { + try { + + List sampleData = SampleDataDao.instance.findAll( + Filters.eq("_id.apiCollectionId", apiCollectionId) + ); + ApiCollection apiCollection = ApiCollectionsDao.instance.findOne("_id", apiCollectionId); + if (apiCollection == null) { + return ERROR.toUpperCase(); + } + String host = apiCollection.getHostName(); + SampleDataToSTI sampleDataToSTI = new SampleDataToSTI(); + sampleDataToSTI.setSampleDataToSTI(sampleData); + Map>>> stiList = sampleDataToSTI.getSingleTypeInfoMap(); + OpenAPI openAPI = Main.init(apiCollection.getDisplayName(),stiList, includeHeaders, host); + openAPIString = Main.convertOpenApiToJSON(openAPI); + } catch (Exception e) { + logger.error("ERROR while downloading openApi file " + e); + return ERROR.toUpperCase(); + } + + return SUCCESS.toUpperCase(); + } + + public String burpSwagger() throws IOException { + setIncludeHeaders(false); + execute(); + + servletResponse.setHeader("Content-Type", "application/json"); + try (PrintWriter writer = servletResponse.getWriter()) { + writer.write(openAPIString); + servletResponse.setStatus(200); + } catch (Exception e) { + servletResponse.sendError(500); + } + + return null; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public String getOpenAPIString() { + return openAPIString; + } + + protected HttpServletResponse servletResponse; + @Override + public void setServletResponse(HttpServletResponse response) { + this.servletResponse = response; + } + + public void setIncludeHeaders(boolean includeHeaders) { + this.includeHeaders = includeHeaders; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/ParamStateAction.java b/apps/dashboard/src/main/java/com/akto/action/ParamStateAction.java new file mode 100644 index 0000000000..8b3f36ba4f --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/ParamStateAction.java @@ -0,0 +1,66 @@ +package com.akto.action; + +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dto.type.SingleTypeInfo; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Aggregates; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Projections; + +import java.util.ArrayList; +import java.util.List; + +import org.bson.Document; +import org.bson.conversions.Bson; + +public class ParamStateAction extends UserAction { + + @Override + public String execute() { + return SUCCESS.toUpperCase(); + } + + private List privateSingleTypeInfo = new ArrayList<>(); + + public String fetchParamsStatus() { + + List pipeline = new ArrayList<>(); + + String computedJson = "{'$divide': ['$" + SingleTypeInfo._PUBLIC_COUNT + "', '$"+SingleTypeInfo._UNIQUE_COUNT+"']}"; + String computedFieldName = "computedValue"; + + pipeline.add(Aggregates.match(Filters.gt(SingleTypeInfo._UNIQUE_COUNT,0))); + + Bson projections = Projections.fields( + Projections.include( + SingleTypeInfo._API_COLLECTION_ID, SingleTypeInfo._URL, SingleTypeInfo._METHOD, + SingleTypeInfo._IS_HEADER, SingleTypeInfo._IS_URL_PARAM, SingleTypeInfo._PARAM, + SingleTypeInfo._UNIQUE_COUNT, SingleTypeInfo._PUBLIC_COUNT + ), + Projections.computed( + computedFieldName, + Document.parse(computedJson) + ) + ); + + pipeline.add(Aggregates.project(projections)); + + pipeline.add(Aggregates.match(Filters.lte(computedFieldName,SingleTypeInfo.THRESHOLD))); + + pipeline.add(Aggregates.limit(3000)); + + MongoCursor cursor = SingleTypeInfoDao.instance.getMCollection().aggregate(pipeline, SingleTypeInfo.class).cursor(); + + while(cursor.hasNext()) { + SingleTypeInfo singleTypeInfo = cursor.next(); + privateSingleTypeInfo.add(singleTypeInfo); + } + + return SUCCESS.toUpperCase(); + } + + public List getPrivateSingleTypeInfo() { + return privateSingleTypeInfo; + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/action/PostmanAction.java b/apps/dashboard/src/main/java/com/akto/action/PostmanAction.java new file mode 100644 index 0000000000..42671a88c7 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/PostmanAction.java @@ -0,0 +1,305 @@ +package com.akto.action; + +import com.akto.ApiRequest; +import com.akto.dao.*; +import com.akto.dao.context.Context; +import com.akto.dto.*; +import com.akto.dto.third_party_access.Credential; +import com.akto.dto.third_party_access.PostmanCredential; +import com.akto.dto.third_party_access.ThirdPartyAccess; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.postman.Main; +import com.akto.utils.DashboardMode; +import com.akto.utils.SampleDataToSTI; +import com.akto.utils.Utils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.ReplaceOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import io.swagger.v3.oas.models.OpenAPI; +import org.json.JSONObject; + +import java.util.*; + +public class PostmanAction extends UserAction { + + private static final Logger logger = LoggerFactory.getLogger(PostmanAction.class); + private static final ObjectMapper mapper = new ObjectMapper(); + @Override + public String execute() { + return SUCCESS; + } + + private String api_key; + private String workspace_id; + public String addOrUpdateApiKey() { + if (api_key == null || workspace_id == null) { + return ERROR.toUpperCase(); + } + User user = getSUser(); + PostmanCredential credential = new PostmanCredential(getSUser().getId()+"", workspace_id, api_key); + ThirdPartyAccess thirdPartyAccess = new ThirdPartyAccess( + Context.now(), user.getId(), 0, credential + ); + + ReplaceOptions replaceOptions= new ReplaceOptions(); + replaceOptions.upsert(true); + ThirdPartyAccessDao.instance.getMCollection().replaceOne( + Filters.and( + Filters.eq("owner", user.getId()), + Filters.eq("credential.type", Credential.Type.POSTMAN) + ), + thirdPartyAccess, + replaceOptions + ); + return SUCCESS.toUpperCase(); + } + + + public void setApi_key(String api_key) { + this.api_key = api_key; + } + + public void setWorkspace_id(String workspace_id) { + this.workspace_id = workspace_id; + } + + + private int apiCollectionId; + public String createPostmanApi() throws Exception { + PostmanCredential postmanCredential = fetchPostmanCredential(); + if (postmanCredential == null) { + addActionError("Please add postman credentials in settings"); + return ERROR.toUpperCase(); + } + + + ApiCollection apiCollection = ApiCollectionsDao.instance.findOne(Filters.eq("_id", apiCollectionId)); + if (apiCollection == null) { + return ERROR.toUpperCase(); + } + String apiName = "AKTO " + apiCollection.getDisplayName(); + + List sampleData = SampleDataDao.instance.findAll( + Filters.eq("_id.apiCollectionId", apiCollectionId) + ); + String host = apiCollection.getHostName(); + SampleDataToSTI sampleDataToSTI = new SampleDataToSTI(); + sampleDataToSTI.setSampleDataToSTI(sampleData); + Map>>> stiList = sampleDataToSTI.getSingleTypeInfoMap(); + OpenAPI openAPI = com.akto.open_api.Main.init(apiCollection.getDisplayName(),stiList, true, host); + String openAPIStringAll = com.akto.open_api.Main.convertOpenApiToJSON(openAPI); + + List SensitiveSampleData = SensitiveSampleDataDao.instance.findAll( + Filters.eq("_id.apiCollectionId", apiCollectionId) + ); + SampleDataToSTI sensitiveSampleDataToSTI = new SampleDataToSTI(); + sensitiveSampleDataToSTI.setSensitiveSampleDataToSTI(SensitiveSampleData); + Map>>> sensitiveStiList = sensitiveSampleDataToSTI.getSingleTypeInfoMap(); + openAPI = com.akto.open_api.Main.init(apiCollection.getDisplayName(), sensitiveStiList, true, host); + String openAPIStringSensitive = com.akto.open_api.Main.convertOpenApiToJSON(openAPI); + + Main main = new Main(postmanCredential.getApiKey()); + Map openApiSchemaMap = new HashMap<>(); + openApiSchemaMap.put("All", openAPIStringAll); + openApiSchemaMap.put("Sensitive", openAPIStringSensitive); + + main.createApiWithSchema(postmanCredential.getWorkspaceId(),apiName, openApiSchemaMap); + + return SUCCESS.toUpperCase(); + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + private String postmanCollectionId; + public String savePostmanCollection() { + int userId = getSUser().getId(); + PostmanCredential postmanCredential = fetchPostmanCredential(); + if (postmanCredential == null) { + addActionError("Please add postman credentials in settings"); + return ERROR.toUpperCase(); + } + + + Main main = new Main(postmanCredential.getApiKey()); + JsonNode postmanCollection = main.fetchPostmanCollectionString(postmanCollectionId); + String collectionName = postmanCollection.get("collection").get("info").get("name").asText(); + String postmanCollectionString = postmanCollection.get("collection").toString(); + + String node_url = System.getenv("AKTO_NODE_URL"); + if (node_url == null) { + + } + String url = node_url+ "/api/postman/endpoints"; + + JSONObject requestBody = new JSONObject(); + requestBody.put("postman_string", postmanCollectionString); + + String json = requestBody.toString(); + JsonNode node = ApiRequest.postRequest(new HashMap<>(), url,json); + JsonNode valueNode = node.get("open_api"); + String open_api_from_postman = valueNode.textValue(); + + APISpec apiSpec = new APISpec(APISpec.Type.JSON, userId,collectionName,open_api_from_postman, apiCollectionId); + APISpecDao.instance.replaceOne(Filters.eq("apiCollectionId", apiCollectionId), apiSpec); + + return SUCCESS.toUpperCase(); + } + + private final BasicDBObject postmanCred = new BasicDBObject(); + public String fetchPostmanCred() { + PostmanCredential postmanCredential = fetchPostmanCredential(); + if (postmanCredential != null) { + postmanCred.put("api_key",postmanCredential.getApiKey()); + postmanCred.put("workspace_id",postmanCredential.getWorkspaceId()); + } + return SUCCESS.toUpperCase(); + } + + + private PostmanCredential fetchPostmanCredential() { + User u = getSUser(); + int userId = u.getId(); + return Utils.fetchPostmanCredential(userId); + } + + private List workspaces; + public String fetchWorkspaces() { + workspaces = new ArrayList<>(); + if (api_key == null || api_key.isEmpty()) { + return SUCCESS.toUpperCase(); + } + + Main main = new Main(api_key); + JsonNode postmanCollection = main.fetchWorkspaces(); + + if (postmanCollection == null) return SUCCESS.toUpperCase(); + Iterator a = postmanCollection.elements(); + while (a.hasNext()) { + JsonNode node = a.next(); + BasicDBObject workspace = new BasicDBObject(); + workspace.put("id", node.get("id").asText()); + workspace.put("name", node.get("name").asText()); + workspace.put("type", node.get("type").asText()); + workspaces.add(workspace); + } + return SUCCESS.toUpperCase(); + } + + List collections = new ArrayList<>(); + public String fetchCollections() { + PostmanCredential postmanCredential = fetchPostmanCredential(); + if (postmanCredential == null) { + addActionError("Please add postman credentials in settings"); + return ERROR.toUpperCase(); + } + + Main main = new Main(postmanCredential.getApiKey()); + JsonNode postmanCollectionsNode = main.fetchApiCollections(); + + Iterator a = postmanCollectionsNode.elements(); + while (a.hasNext()) { + JsonNode node = a.next(); + BasicDBObject collection = new BasicDBObject(); + collection.put("uid", node.get("uid").asText()); + collection.put("name", node.get("name").asText()); + collections.add(collection); + } + + return SUCCESS.toUpperCase(); + } + + private boolean skipKafka = DashboardMode.isLocalDeployment(); + + public String importDataFromPostman() throws Exception { + PostmanCredential postmanCredential = fetchPostmanCredential(); + if (postmanCredential == null) { + addActionError("Please add postman credentials in settings"); + return ERROR.toUpperCase(); + } + Main main = new Main(postmanCredential.getApiKey()); + String workspaceId = this.workspace_id; + logger.info("Fetching details for workspace_id: {}", workspace_id); + JsonNode workspaceDetails = main.fetchWorkspace(workspaceId); + JsonNode workspaceObj = workspaceDetails.get("workspace"); + ArrayNode collectionsObj = (ArrayNode) workspaceObj.get("collections"); + Map> aktoFormat = new HashMap<>(); + for(JsonNode collectionObj: collectionsObj){ + List msgs = new ArrayList<>(); + String collectionId = collectionObj.get("id").asText(); + int aktoCollectionId = collectionId.hashCode(); + aktoCollectionId = aktoCollectionId < 0 ? aktoCollectionId * -1: aktoCollectionId; + JsonNode collectionDetails = main.fetchCollection(collectionId); + JsonNode collectionDetailsObj = collectionDetails.get("collection"); + Map variablesMap = Utils.getVariableMap((ArrayNode) collectionDetailsObj.get("variable")); + ArrayList jsonNodes = new ArrayList<>(); + Utils.fetchApisRecursively((ArrayNode) collectionDetailsObj.get("item"), jsonNodes); + String collectionName = collectionDetailsObj.get("info").get("name").asText(); + if(jsonNodes.size() == 0) { + logger.info("Collection {} has no requests, skipping it", collectionName); + continue; + } + logger.info("Found {} apis in collection {}", jsonNodes.size(), collectionName); + for(JsonNode item: jsonNodes){ + String apiName = item.get("name").asText(); + logger.info("Processing api {} if collection {}", apiName, collectionName); + Map apiInAktoFormat = Utils.convertApiInAktoFormat(item, variablesMap, String.valueOf(1_000_000)); + if(apiInAktoFormat != null){ + try{ + apiInAktoFormat.put("akto_vxlan_id", String.valueOf(aktoCollectionId)); + String s = mapper.writeValueAsString(apiInAktoFormat); + logger.info("Api name: {}, CollectionName: {}, AktoFormat: {}", apiName, collectionName, s); + msgs.add(s); + } catch (JsonProcessingException e){ + logger.error(e.getMessage(), e); + } + } + } + if(msgs.size() > 0) { + aktoFormat.put(aktoCollectionId, msgs); + if(ApiCollectionsDao.instance.findOne(Filters.eq("_id", aktoCollectionId)) == null){ + ApiCollectionsDao.instance.insertOne(ApiCollection.createManualCollection(aktoCollectionId, "Postman " + collectionName)); + } + logger.info("Pushed {} apis from collection {}", msgs.size(), collectionName); + } + + } + //Push to Akto + logger.info("Starting to push data to Akto, pushin data in {} collections", aktoFormat.size()); + String topic = System.getenv("AKTO_KAFKA_TOPIC_NAME"); + for(Map.Entry> entry: aktoFormat.entrySet()){ + //For each entry, push a message to Kafka + int aktoCollectionId = entry.getKey(); + List msgs = entry.getValue(); + Utils.pushDataToKafka(aktoCollectionId, topic, msgs, new ArrayList<>(), skipKafka); + logger.info("Pushed data in apicollection id {}", aktoCollectionId); + } + return SUCCESS.toUpperCase(); + } + + + public List getCollections() { + return collections; + } + + public void setPostmanCollectionId(String postmanCollectionId) { + this.postmanCollectionId = postmanCollectionId; + } + + public List getWorkspaces() { + return workspaces; + } + + public BasicDBObject getPostmanCred() { + return postmanCred; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/ProfileAction.java b/apps/dashboard/src/main/java/com/akto/action/ProfileAction.java new file mode 100644 index 0000000000..05f4ef4f42 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/ProfileAction.java @@ -0,0 +1,152 @@ +package com.akto.action; + + +import com.akto.dao.AccountsDao; +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.*; +import com.akto.utils.DashboardMode; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import org.bson.internal.Base64; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.mongodb.client.model.Filters.*; + +public class ProfileAction extends UserAction { + + private int accountId; + + @Override + public String execute() { + + return SUCCESS.toUpperCase(); + } + + public static void executeMeta1(User user, HttpServletRequest request) { + BasicDBObject userDetails = new BasicDBObject(); + BasicDBObject accounts = new BasicDBObject(); + Integer sessionAccId = Context.accountId.get(); + if (sessionAccId == null || sessionAccId == 0) { + sessionAccId = 0; + } + + List accountIdInt = new ArrayList<>(); + for (UserAccountEntry uae: user.getAccounts().values()) { + accountIdInt.add(uae.getAccountId()); + if (uae.isDefault() && sessionAccId == 0) { + sessionAccId = uae.getAccountId(); + } + } + + for (Account acc: AccountsDao.instance.findAll(in("_id", accountIdInt))) { + accounts.append(acc.getId()+"", acc.getName()); + if (sessionAccId == 0) { + sessionAccId = acc.getId(); + } + }; + + if (sessionAccId == 0) { + throw new IllegalStateException("user has no accounts associated"); + } else { + request.getSession().setAttribute("accountId", sessionAccId); + Context.accountId.set(sessionAccId); + } + + BasicDBList listDashboards = new BasicDBList(); + + userDetails.append("accounts", accounts) + .append("username",user.getName()) + .append("avatar", "dummy") + .append("activeAccount", sessionAccId) + .append("dashboardMode", DashboardMode.getDashboardMode()) + .append("users", UsersDao.instance.getAllUsersInfoForTheAccount(Context.accountId.get())); + + for (String k: userDetails.keySet()) { + request.setAttribute(k, userDetails.get(k)); + } + + return; + } + + private String type; + private String username; + private String avatar; + private Collection accounts; + private UserAccountEntry activeAccount; + private BasicDBList users; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Collection getAccounts() { + return accounts; + } + + public void setAccounts(Collection userAccountEntries) { + this.accounts = userAccountEntries; + } + + public UserAccountEntry getActiveAccount() { + return activeAccount; + } + + public void setActiveAccount(UserAccountEntry userAccountEntry) { + this.activeAccount = userAccountEntry; + } + + public int getAccountId() { + return accountId; + } + + public void setAccountId(int accountId) { + this.accountId = accountId; + } + + public BasicDBList getUsers() { + return users; + } + + private BasicDBObject subscription; + + public BasicDBObject getSubscription() { + return subscription; + } + + public void setSubscription(BasicDBObject subscription) { + this.subscription = subscription; + } + + public String saveSubscription() { + User user = getSUser(); + UsersDao.instance.insertPushSubscription(user.getLogin(), subscription); + subscription = new BasicDBObject("complete", true); + return SUCCESS.toUpperCase(); + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/SensitiveFieldAction.java b/apps/dashboard/src/main/java/com/akto/action/SensitiveFieldAction.java new file mode 100644 index 0000000000..a408d473eb --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/SensitiveFieldAction.java @@ -0,0 +1,217 @@ +package com.akto.action; + +import com.akto.DaoInit; +import com.akto.dao.RelationshipDao; +import com.akto.dao.SensitiveParamInfoDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.context.Context; +import com.akto.dto.Relationship; +import com.akto.dto.SensitiveParamInfo; +import com.akto.dto.type.SingleTypeInfo; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.bulk.BulkWriteResult; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.FindOneAndUpdateOptions; +import com.mongodb.client.model.UpdateOneModel; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.Updates; +import com.mongodb.client.model.WriteModel; +import com.opensymphony.xwork2.Action; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; + +import java.util.*; + +public class SensitiveFieldAction extends UserAction{ + + private String url; + private String method; + private int responseCode; + private boolean isHeader; + private String param; + private int apiCollectionId; + private boolean sensitive; + + private BasicDBObject ret; + + @Override + public String execute() { + ret = new BasicDBObject(); + Bson filter = SensitiveParamInfoDao.getFilters(url, method, responseCode, isHeader, param, apiCollectionId); + // null means user wants Akto to decide the sensitivity + if (!sensitive) { + SensitiveParamInfoDao.instance.getMCollection().deleteOne(filter); + return Action.SUCCESS.toUpperCase(); + } + + Bson update = Updates.set("sensitive", sensitive); + FindOneAndUpdateOptions findOneAndUpdateOptions = new FindOneAndUpdateOptions(); + findOneAndUpdateOptions.upsert(false); + SensitiveParamInfo param = SensitiveParamInfoDao.instance.updateOne(filter, update); + + ret.append("data", param); + + // only if sensitive is marked true then only find other params + if (!sensitive) { + return Action.SUCCESS.toUpperCase(); + } + + /* + + Bson parentFilters = Filters.and( + Filters.eq("parent.url", url), + Filters.eq("parent.method", method), + Filters.eq("parent.isHeader", isHeader), + Filters.eq("parent.param", param) + ); + + Bson childFilters = Filters.and( + Filters.eq("child.url", url), + Filters.eq("child.method", method), + Filters.eq("child.isHeader", isHeader), + Filters.eq("child.param", param) + ); + + // find all relationships based on either parent or child matches with the param user marked sensitive + List relationshipList = RelationshipDao.instance.findAll( + Filters.or(parentFilters,childFilters) + ); + + // either parent or child will match. We take parent to be default and if it matches then child is the related one + List filtersForSingleTypeInfo = new ArrayList<>(); + for (Relationship relationship: relationshipList) { + Relationship.ApiRelationInfo main = relationship.getParent(); + if (Objects.equals(main.getUrl(), url) && Objects.equals(main.getMethod(), method) && + Objects.equals(main.getParam(), param) && main.isHeader() == isHeader && main.getResponseCode() == responseCode) { + main = relationship.getChild(); + } + filtersForSingleTypeInfo.add(getFiltersForApiRelation(main)); + } + + if (filtersForSingleTypeInfo.isEmpty()) { + return Action.SUCCESS.toUpperCase(); + } + + relatedSingleTypeInfo = SingleTypeInfoDao.instance.findAll(Filters.or(filtersForSingleTypeInfo)); + + if (relatedSingleTypeInfo.isEmpty()) { + return Action.SUCCESS.toUpperCase(); + } + + // find and remove those which user has already marked sensitive + List filtersForAdditionalSensitiveParams = new ArrayList<>(); + for (SingleTypeInfo singleTypeInfo: relatedSingleTypeInfo) { + filtersForAdditionalSensitiveParams.add( + getFilters( + singleTypeInfo.getUrl(), singleTypeInfo.getMethod(), singleTypeInfo.getResponseCode(), + singleTypeInfo.isIsHeader(), singleTypeInfo.getParam() + ) + ); + } + + List sensitiveParamInfoList = SensitiveParamInfoDao.instance.findAll( + Filters.or(filtersForAdditionalSensitiveParams) + ); + + Set sensitiveParamInfoSet = new HashSet<>(); + for (SensitiveParamInfo sensitiveParamInfo: sensitiveParamInfoList) { + String key = sensitiveParamInfo.getUrl() + "." + sensitiveParamInfo.getMethod() + "." + sensitiveParamInfo.getParam() + "." + sensitiveParamInfo.getResponseCode(); + sensitiveParamInfoSet.add(key); + } + + Iterator i = relatedSingleTypeInfo.iterator(); + while (i.hasNext()) { + SingleTypeInfo singleTypeInfo = i.next(); + String key = singleTypeInfo.getUrl() + "." + singleTypeInfo.getMethod() + "." + singleTypeInfo.getParam() + "." + singleTypeInfo.getResponseCode(); + if (sensitiveParamInfoSet.contains(key)) { + i.remove(); + } + } + + */ + + return Action.SUCCESS.toUpperCase(); + } + + + public String listAllSensitiveFields() { + List sensitiveParams = SensitiveParamInfoDao.instance.findAll(Filters.eq("sensitive", true)); + ret = new BasicDBObject(); + ret.append("data", sensitiveParams); + return Action.SUCCESS.toUpperCase(); + } + + + // private static Bson getFiltersForApiRelation(Relationship.ApiRelationInfo apiRelationInfo) { + // return getFilters(apiRelationInfo.getUrl(), apiRelationInfo.getMethod(), apiRelationInfo.getResponseCode(), + // apiRelationInfo.isHeader(), apiRelationInfo.getParam()); + // } + + private BasicDBList items; + public String bulkMarkSensitive() { + ArrayList> bulkUpdates = new ArrayList<>(); + for (Object item: items) { + HashMap xObj = (HashMap) item; + String url = xObj.get("url").toString(); + String method = xObj.get("method").toString(); + long responseCode = (Long) xObj.get("responseCode"); + boolean isHeader = (Boolean) xObj.get("isHeader"); + String param = xObj.get("param").toString(); + long apiCollectionId = Long.parseLong(xObj.get("apiCollectionId").toString()); + Bson filter = SensitiveParamInfoDao.getFilters(url, method, (int) responseCode, isHeader, param, (int) apiCollectionId); + + Bson bson = Updates.set("sensitive", sensitive); + + bulkUpdates.add( + new UpdateOneModel<>(filter, bson, new UpdateOptions().upsert(true)) + ); + } + + BulkWriteResult res = SensitiveParamInfoDao.instance.getMCollection().bulkWrite(bulkUpdates); + ret = new BasicDBObject(); + ret.append("updates", res.getModifiedCount()).append("inserts", res.getInsertedCount()); + return Action.SUCCESS.toUpperCase(); + } + + public void setUrl(String url) { + this.url = url; + } + + public void setMethod(String method) { + this.method = method; + } + + public void setResponseCode(int responseCode) { + this.responseCode = responseCode; + } + + public void setIsHeader(boolean isHeader) { + this.isHeader = isHeader; + } + + public void setParam(String param) { + this.param = param; + } + + public void setApiCollectionId (int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public void setSensitive(Boolean sensitive) { + this.sensitive = sensitive; + } + + public void setRet(BasicDBObject ret) { + this.ret = ret; + } + + public BasicDBObject getRet() { + return this.ret; + } + + public void setItems(BasicDBList items) { + this.items = items; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/SignupAction.java b/apps/dashboard/src/main/java/com/akto/action/SignupAction.java new file mode 100644 index 0000000000..303d99878f --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/SignupAction.java @@ -0,0 +1,405 @@ +package com.akto.action; + +import com.akto.dao.*; +import com.akto.dto.*; +import com.akto.listener.InitializerListener; +import com.akto.utils.JWT; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest; +import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; +import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; +import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.mongodb.BasicDBObject; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Sorts; +import com.opensymphony.xwork2.Action; +import com.sendgrid.helpers.mail.Mail; +import com.slack.api.Slack; +import com.slack.api.methods.SlackApiException; +import com.slack.api.methods.request.oauth.OAuthV2AccessRequest; +import com.slack.api.methods.request.users.UsersIdentityRequest; +import com.slack.api.methods.response.oauth.OAuthV2AccessResponse; +import com.slack.api.methods.response.users.UsersIdentityResponse; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import org.apache.commons.lang3.StringUtils; +import org.apache.struts2.interceptor.ServletRequestAware; +import org.apache.struts2.interceptor.ServletResponseAware; +import org.bson.conversions.Bson; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +import static com.mongodb.client.model.Filters.all; +import static com.mongodb.client.model.Filters.eq; +import static com.mongodb.client.model.Updates.combine; +import static com.mongodb.client.model.Updates.set; + +public class SignupAction implements Action, ServletResponseAware, ServletRequestAware { + + public static final String SIGN_IN = "signin"; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + String code; + String state; + public String registerViaSlack() { + String codeFromSlack = code; + code = "err"; + + Config.SlackConfig aktoSlackConfig = (Config.SlackConfig) ConfigsDao.instance.findOne("_id", "SLACK-ankush"); + + if(aktoSlackConfig == null) { + return Action.ERROR.toUpperCase(); + } + + OAuthV2AccessRequest request = OAuthV2AccessRequest.builder() + .clientId(aktoSlackConfig.getClientId()) + .code(codeFromSlack) + .clientSecret(aktoSlackConfig.getClientSecret()) + .redirectUri(aktoSlackConfig.getRedirect_url()).build(); + + try { + OAuthV2AccessResponse response = Slack.getInstance().methods().oauthV2Access(request); + String error = response.getError(); + + if (error != null && error.length() > 0) { + code = error; + } else { + + OAuthV2AccessResponse.AuthedUser authedUser = response.getAuthedUser(); + + String enterpriseName = "", enterpriseId = "", teamName = "", teamId = ""; + if (response.getEnterprise() != null) { + enterpriseId = response.getEnterprise().getId(); + enterpriseName = response.getEnterprise().getName(); + } + + if (response.getTeam() != null) { + teamId = response.getTeam().getId(); + teamName = response.getTeam().getName(); + } + + SignupInfo.SlackSignupInfo info = + new SignupInfo.SlackSignupInfo( + aktoSlackConfig.getId(), + response.isOk(), + response.getAppId(), + authedUser.getAccessToken(), + authedUser.getId(), + authedUser.getScope(), + authedUser.getTokenType(), + response.getScope(), + response.getTokenType(), + response.getAccessToken(), + response.getBotUserId(), + teamName, + teamId, + enterpriseName, + enterpriseId, + response.isEnterpriseInstall() + ); + + + UsersIdentityRequest usersIdentityRequest = UsersIdentityRequest.builder().token(authedUser.getAccessToken()).build(); + UsersIdentityResponse usersIdentityResponse = Slack.getInstance().methods(info.getAuthedUserAccessToken()).usersIdentity(usersIdentityRequest); + + if (usersIdentityResponse.isOk()) { + UsersIdentityResponse.User slackUser = usersIdentityResponse.getUser(); + String userName = slackUser.getName(); + String userEmail = slackUser.getEmail(); + String userSlackId = slackUser.getId(); + + if(userSlackId != null && !userSlackId.equalsIgnoreCase(info.getAuthedUserId())) { + throw new IllegalStateException("user id mismatch between slack identity and token authed user"); + } + + code = ""; + createUserAndRedirect(userEmail, userName, info); + } else { + code = usersIdentityResponse.getError(); + } + } + + } catch (IOException e) { + code = e.getMessage(); + } catch (SlackApiException e) { + code = e.getResponse().message(); + } catch (Exception e) { + ; + code = e.getMessage(); + } finally { + if (code.length() > 0) { + return Action.ERROR.toUpperCase(); + } else { + return Action.SUCCESS.toUpperCase(); + } + } + + } + + public String registerViaGoogle() { + + String codeFromGoogle = code; + code = "err"; + + Config.GoogleConfig aktoGoogleConfig = (Config.GoogleConfig) ConfigsDao.instance.findOne("_id", "GOOGLE-ankush"); + if (aktoGoogleConfig == null) { + + } + + + GoogleClientSecrets clientSecrets = new GoogleClientSecrets(); + GoogleClientSecrets.Details details = new GoogleClientSecrets.Details(); + details.setAuthUri(aktoGoogleConfig.getAuthURI()); + details.setClientId(aktoGoogleConfig.getClientId()); + details.setClientSecret(aktoGoogleConfig.getClientSecret()); + details.setTokenUri(aktoGoogleConfig.getTokenURI()); + clientSecrets.setWeb(details); + + GoogleTokenResponse tokenResponse = + null; + try { + tokenResponse = new GoogleAuthorizationCodeTokenRequest( + new NetHttpTransport(), + JacksonFactory.getDefaultInstance(), + clientSecrets.getDetails().getTokenUri(), + clientSecrets.getDetails().getClientId(), + clientSecrets.getDetails().getClientSecret(), + codeFromGoogle, + InitializerListener.getDomain()+"/signup-google") + .execute(); + + String accessToken = tokenResponse.getAccessToken(); + String refreshToken = tokenResponse.getRefreshToken(); + GoogleIdToken.Payload payload = tokenResponse.parseIdToken().getPayload(); + + String username = (String) payload.get("name"); + String userEmail = payload.getEmail(); + + SignupInfo.GoogleSignupInfo signupInfo = new SignupInfo.GoogleSignupInfo(aktoGoogleConfig.getId(), accessToken, refreshToken, tokenResponse.getExpiresInSeconds()); + createUserAndRedirect(userEmail, username, signupInfo); + code = ""; + } catch (IOException e) { + code = e.getMessage(); + return ERROR.toUpperCase(); + + } + return SUCCESS.toUpperCase(); + + }; + + String password; + String email; + String invitationCode; + + public String registerViaEmail() { + code = ""; + if (password != null ) { + code = validatePassword(password); + if (code != null) return ERROR.toUpperCase(); + } else { + code = "Password can't be empty"; + return ERROR.toUpperCase(); + } + long count = UsersDao.instance.getMCollection().countDocuments(); + // only 1st user is allowed to signup without invitationCode + if (count != 0) { + Jws jws; + try { + jws = JWT.parseJwt(invitationCode, ""); + } catch (Exception e) { + code = "Ask admin to invite you"; + return ERROR.toUpperCase(); + } + + String emailFromJwt = jws.getBody().get("email").toString(); + if (!emailFromJwt.equals(email)) { + code = "Ask admin to invite you"; + return ERROR.toUpperCase(); + } + + Bson filter = Filters.eq(PendingInviteCode.INVITE_CODE, invitationCode); + PendingInviteCode pendingInviteCode = PendingInviteCodesDao.instance.findOne(filter); + + if (pendingInviteCode == null) { + code = "Ask admin to invite you. If you are already a user, please click on login"; + return ERROR.toUpperCase(); + } + + // deleting the invitation code + PendingInviteCodesDao.instance.getMCollection().deleteOne(filter); + + if (UsersDao.instance.findOne("login", email) != null) { + code = "This "; + return ERROR.toUpperCase(); + } + } + + String salt = "39yu"; + String passHash = Integer.toString((salt + password).hashCode()); + + SignupInfo.PasswordHashInfo signupInfo = new SignupInfo.PasswordHashInfo(passHash, salt); + try { + createUserAndRedirect(email, email, signupInfo); + } catch (IOException e) { + ; + return ERROR.toUpperCase(); + } + return SUCCESS.toUpperCase(); + } + + public static final String MINIMUM_PASSWORD_ERROR = "Minimum of 8 characters required"; + public static final String MAXIMUM_PASSWORD_ERROR = "Maximum of 40 characters allowed"; + public static final String INVALID_CHAR = "Invalid character"; + public static final String MUST_BE_ALPHANUMERIC_ERROR = "Must contain letters and numbers"; + public static String validatePassword(String password) { + boolean minimumFlag = password.length() >= 8; + if (!minimumFlag) return MINIMUM_PASSWORD_ERROR; + boolean maximumFlag = password.length() < 40; + if (!maximumFlag) return MAXIMUM_PASSWORD_ERROR; + + boolean numbersFlag = false; + boolean lettersFlag = false; + + Set allowedSpecialChars = new HashSet<>(Arrays.asList("+", "@", "*", "#", "$", "%", "&", "/", "(", ")", "=", "?", "^", "!", "[","]", "{", "}", "-", "_", ":", ";", ">", "<", "|", ",", ".")); + + for (int i = 0; i < password.length(); i++) { + char ch = password.charAt(i); + char upperCaseCh = Character.toUpperCase(ch); + if (ch >= '0' && ch <= '9') { + numbersFlag = true; + } else if (upperCaseCh >= 'A' && upperCaseCh <= 'Z') { + lettersFlag = true; + } else if (!allowedSpecialChars.contains(ch+"")) { + return INVALID_CHAR; + } + } + + if (numbersFlag && lettersFlag) { + return null; + } + + return MUST_BE_ALPHANUMERIC_ERROR; + + } + + String companyName, teamName; + List allEmails; + String shouldLogin="false"; + + private void createUserAndRedirect(String userEmail, String username, SignupInfo signupInfo) throws IOException { + String accountName = System.getenv("AKTO_ACCOUNT_NAME"); + if (accountName == null) accountName = "Helios"; + Account account = AccountsDao.instance.findOne("name", accountName); + if(!SIGN_IN.equals(state)) { + if (account == null) { + account = new Account(1000000, accountName); + AccountsDao.instance.insertOne(account); + } + } + + int accountId = account.getId(); + User user = UsersDao.instance.insertSignUp(userEmail, username, signupInfo, accountId); + long count = UsersDao.instance.getMCollection().countDocuments(); + // if first user then automatic admin + // else check if rbac is 0 or not. If 0 then make the user that was created first as admin. + // done for customers who were there before rbac feature + if (count == 1) { + RBACDao.instance.insertOne(new RBAC(user.getId(), RBAC.Role.ADMIN)); + } else { + long rbacCount = RBACDao.instance.getMCollection().countDocuments(); + if (rbacCount == 0) { + MongoCursor cursor = UsersDao.instance.getMCollection().find().sort(Sorts.ascending("_id")).limit(1).cursor(); + if (cursor.hasNext()) { + User firstUser = cursor.next(); + RBACDao.instance.insertOne(new RBAC(firstUser.getId(), RBAC.Role.ADMIN)); + } + } + } + + servletRequest.getSession().setAttribute("user", user); + new LoginAction().loginUser(user, servletResponse, true, servletRequest); + servletResponse.sendRedirect("/dashboard/testing"); + + } + + protected HttpServletResponse servletResponse; + @Override + public void setServletResponse(HttpServletResponse httpServletResponse) { + this.servletResponse= httpServletResponse; + } + + @Override + public String execute() throws Exception { + return SUCCESS; + } + + protected HttpServletRequest servletRequest; + @Override + public void setServletRequest(HttpServletRequest httpServletRequest) { + this.servletRequest = httpServletRequest; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getCompanyName() { + return companyName; + } + + public void setCompanyName(String companyName) { + this.companyName = companyName; + } + + public String getTeamName() { + return teamName; + } + + public void setTeamName(String teamName) { + this.teamName = teamName; + } + + public List getAllEmails() { + return allEmails; + } + + public void setAllEmails(List allEmails) { + this.allEmails = allEmails; + } + + public void setInvitationCode(String invitationCode) { + this.invitationCode = invitationCode; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/TagConfigsAction.java b/apps/dashboard/src/main/java/com/akto/action/TagConfigsAction.java new file mode 100644 index 0000000000..040b4cae24 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/TagConfigsAction.java @@ -0,0 +1,214 @@ +package com.akto.action; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.akto.action.CustomDataTypeAction.ConditionFromUser; +import com.akto.dao.TagConfigsDao; +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.CustomDataType; +import com.akto.dto.TagConfig; +import com.akto.dto.User; +import com.akto.dto.data_types.Conditions; +import com.akto.dto.data_types.Predicate; +import com.akto.utils.AktoCustomException; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.FindOneAndUpdateOptions; +import com.mongodb.client.model.ReturnDocument; +import com.mongodb.client.model.Updates; +import com.opensymphony.xwork2.Action; + +public class TagConfigsAction extends UserAction { + + private BasicDBObject tagConfigs; + public String fetchTagConfigs() { + List tagConfigs = TagConfigsDao.instance.findAll(new BasicDBObject()); + Collections.reverse(tagConfigs); + + Set userIds = new HashSet<>(); + for (TagConfig customDataType: tagConfigs) { + userIds.add(customDataType.getCreatorId()); + } + userIds.add(getSUser().getId()); + + Map usersMap = UsersDao.instance.getUsernames(userIds); + + this.tagConfigs = new BasicDBObject(); + this.tagConfigs.put("tagConfigs", tagConfigs); + this.tagConfigs.put("usersMap", usersMap); + + return Action.SUCCESS.toUpperCase(); + } + + private TagConfig tagConfig = null; + private boolean createNew = false; + private String name = null; + private boolean active = false; + private String keyOperator; + private List keyConditionFromUsers; + + + public TagConfig generateTagConfig(int userId) throws AktoCustomException { + if (name == null || name.length() == 0) throw new AktoCustomException("Name cannot be empty"); + if (name.split(" ").length > 1) throw new AktoCustomException("Name has to be single word"); + name = name.trim(); + name = name.toUpperCase(); + if (!(name.matches("[A-Z_0-9]+"))) throw new AktoCustomException("Name can only contain alphabets, numbers and underscores"); + + if (keyOperator == null) { + keyOperator = "AND"; + } + + Conditions keyConditions = null; + Conditions.Operator kOperator = Conditions.Operator.valueOf(keyOperator); + if (keyConditionFromUsers != null) { + List predicates = new ArrayList<>(); + for (ConditionFromUser conditionFromUser: keyConditionFromUsers) { + Predicate predicate = Predicate.generatePredicate(conditionFromUser.type, conditionFromUser.valueMap); + if (predicate == null) { + throw new AktoCustomException("Invalid key conditions"); + } else { + predicates.add(predicate); + } + } + + if (predicates.size() > 0) { + keyConditions = new Conditions(predicates, kOperator); + } + } + + if (keyConditions == null || keyConditions.getPredicates() == null || keyConditions.getPredicates().size() == 0) { + + throw new AktoCustomException("Both key and value conditions can't be empty"); + } + + return new TagConfig(name, userId, true, keyConditions); + } + + + public String saveTagConfig() { + User user = getSUser(); + tagConfig = null; + try { + tagConfig = generateTagConfig(user.getId()); + } catch (AktoCustomException e) { + addActionError(e.getMessage()); + return ERROR.toUpperCase(); + } + + if (this.createNew) { + TagConfig tagConfigFromDb = TagConfigsDao.instance.findOne(Filters.eq(TagConfig.NAME, name)); + if (tagConfigFromDb != null) { + addActionError("Tag with same name exists"); + return ERROR.toUpperCase(); + } + // id is automatically set when inserting in pojo + TagConfigsDao.instance.insertOne(tagConfig); + } else { + FindOneAndUpdateOptions options = new FindOneAndUpdateOptions(); + options.returnDocument(ReturnDocument.AFTER); + options.upsert(false); + tagConfig = TagConfigsDao.instance.getMCollection().findOneAndUpdate( + Filters.eq("name", tagConfig.getName()), + Updates.combine( + Updates.set(CustomDataType.KEY_CONDITIONS,tagConfig.getKeyConditions()), + Updates.set(CustomDataType.TIMESTAMP,Context.now()), + Updates.set(CustomDataType.ACTIVE,active) + ), + options + ); + + if (tagConfig == null) { + addActionError("Failed to update tag"); + return ERROR.toUpperCase(); + } + } + + return Action.SUCCESS.toUpperCase(); + } + + public String toggleActiveTagConfig() { + FindOneAndUpdateOptions options = new FindOneAndUpdateOptions(); + options.returnDocument(ReturnDocument.AFTER); + options.upsert(false); + tagConfig = TagConfigsDao.instance.getMCollection().findOneAndUpdate( + Filters.eq(CustomDataType.NAME, this.name), + Updates.set(CustomDataType.ACTIVE, active), + options + ); + + if (tagConfig == null) { + String v = active ? "activate" : "deactivate"; + addActionError("Failed to "+ v +" data type"); + return ERROR.toUpperCase(); + } + + return Action.SUCCESS.toUpperCase(); + } + + public BasicDBObject getTagConfigs() { + return this.tagConfigs; + } + + public void setTagConfigs(BasicDBObject tagConfigs) { + this.tagConfigs = tagConfigs; + } + + public TagConfig getTagConfig() { + return this.tagConfig; + } + + public void setTagConfig(TagConfig tagConfig) { + this.tagConfig = tagConfig; + } + + public boolean getCreateNew() { + return this.createNew; + } + + public void setCreateNew(boolean createNew) { + this.createNew = createNew; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isActive() { + return this.active; + } + + public boolean getActive() { + return this.active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public String getKeyOperator() { + return this.keyOperator; + } + + public void setKeyOperator(String keyOperator) { + this.keyOperator = keyOperator; + } + + public List getKeyConditionFromUsers() { + return this.keyConditionFromUsers; + } + + public void setKeyConditionFromUsers(List keyConditionFromUsers) { + this.keyConditionFromUsers = keyConditionFromUsers; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/TeamAction.java b/apps/dashboard/src/main/java/com/akto/action/TeamAction.java new file mode 100644 index 0000000000..2255e24c20 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/TeamAction.java @@ -0,0 +1,108 @@ +package com.akto.action; + +import com.akto.dao.PendingInviteCodesDao; +import com.akto.dao.RBACDao; +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.PendingInviteCode; +import com.akto.dto.RBAC; +import com.akto.dto.RBAC.Role; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.mongodb.client.result.DeleteResult; +import com.opensymphony.xwork2.Action; + +import org.bson.conversions.Bson; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TeamAction extends UserAction { + + int id; + BasicDBList users; + + public String fetchTeamData() { + List allRoles = RBACDao.instance.findAll(new BasicDBObject()); + + Map userToRBAC = new HashMap<>(); + for(RBAC rbac: allRoles) { + userToRBAC.put(rbac.getUserId(), rbac); + } + + users = UsersDao.instance.getAllUsersInfoForTheAccount(Context.accountId.get()); + for(Object obj: users) { + BasicDBObject userObj = (BasicDBObject) obj; + RBAC rbac = userToRBAC.get(userObj.getInt("id")); + String status = rbac == null ? "Member" : rbac.getRole().name(); + userObj.append("role", status); + } + + List pendingInviteCodes = PendingInviteCodesDao.instance.findAll(new BasicDBObject()); + + for(PendingInviteCode pendingInviteCode: pendingInviteCodes) { + users.add( + new BasicDBObject("id", pendingInviteCode.getIssuer()) + .append("login", pendingInviteCode.getInviteeEmailId()) + .append("name", "-") + .append("role", "Invitation sent") + ); + } + + return SUCCESS.toUpperCase(); + } + + String email; + public String removeUser() { + int currUserId = getSUser().getId(); + RBAC record = RBACDao.instance.findOne("userId", currUserId, "role", Role.ADMIN); + if (record == null || getSUser().getLogin().equals(email) || email == null) { + return Action.ERROR.toUpperCase(); + } else { + int accId = Context.accountId.get(); + + Bson findQ = Filters.eq("login", email); + boolean userExists = UsersDao.instance.findOne(findQ) != null; + + if (userExists) { + UsersDao.instance.updateOne(findQ, Updates.unset("accounts."+accId)); + return Action.SUCCESS.toUpperCase(); + } else { + DeleteResult delResult = PendingInviteCodesDao.instance.getMCollection().deleteMany(Filters.eq("inviteeEmailId", email)); + if (delResult.getDeletedCount() > 0) { + return Action.SUCCESS.toUpperCase(); + } else { + return Action.ERROR.toUpperCase(); + } + } + } + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public BasicDBList getUsers() { + return users; + } + + public void setUsers(BasicDBList users) { + this.users = users; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getEmail() { + return this.email; + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/action/TrafficAction.java b/apps/dashboard/src/main/java/com/akto/action/TrafficAction.java new file mode 100644 index 0000000000..77ed65e5e1 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/TrafficAction.java @@ -0,0 +1,130 @@ +package com.akto.action; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.akto.dao.SampleDataDao; +import com.akto.dao.SensitiveSampleDataDao; +import com.akto.dao.TrafficInfoDao; +import com.akto.dao.context.Context; +import com.akto.dto.SensitiveSampleData; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.traffic.TrafficInfo; +import com.akto.dto.type.SingleTypeInfo; +import com.mongodb.client.model.Filters; +import com.opensymphony.xwork2.Action; + +public class TrafficAction { + + int apiCollectionId; + String url; + String method; + int startEpoch; + int endEpoch; + + Map traffic = new HashMap<>(); + + public String fetchEndpointTrafficData() { + traffic = new HashMap<>(); + List trafficInfoList = TrafficInfoDao.instance.findAll(Filters.and( + Filters.eq("_id.url", url), + Filters.eq("_id.apiCollectionId", apiCollectionId), + Filters.eq("_id.responseCode", -1), + Filters.eq("_id.method", method), + Filters.gte("_id.bucketStartEpoch", startEpoch/3600/24/30), + Filters.lte("_id.bucketEndEpoch", endEpoch/3600/24/30 + 1) + )); + + for(TrafficInfo trafficInfo: trafficInfoList) { + for(Map.Entry entry: trafficInfo.mapHoursToCount.entrySet()) { + int count = entry.getValue(); + int startEpoch = Integer.parseInt(entry.getKey()) * 3600; + int yyyyMMdd = Context.convertEpochToDateInt(startEpoch, "US/Pacific"); + traffic.compute(yyyyMMdd, (k, v) -> count + (v == null ? 0 : v)); + } + } + + return Action.SUCCESS.toUpperCase(); + } + + List sampleDataList; + public String fetchSampleData() { + traffic = new HashMap<>(); + sampleDataList = new ArrayList<>(); + sampleDataList = SampleDataDao.instance.findAll(Filters.and( + Filters.eq("_id.url", url), + Filters.eq("_id.apiCollectionId", apiCollectionId), + Filters.eq("_id.responseCode", -1), + Filters.eq("_id.method", method), + Filters.gte("_id.bucketStartEpoch", 0), + Filters.lte("_id.bucketEndEpoch", 0) + )); + + return Action.SUCCESS.toUpperCase(); + } + + Map> sensitiveSampleData = new HashMap<>(); + public String fetchSensitiveSampleData() { + List sensitiveSampleDataList = SensitiveSampleDataDao.instance.findAll( + Filters.and( + Filters.eq("_id.url", url), + Filters.eq("_id.apiCollectionId", apiCollectionId), + Filters.eq("_id.method", method) + ) + ); + + for (SensitiveSampleData sensitiveSampleData: sensitiveSampleDataList) { + for (String data: sensitiveSampleData.getSampleData()) { + List s = this.sensitiveSampleData.getOrDefault(data, new ArrayList<>()); + s.add(sensitiveSampleData.getId()); + this.sensitiveSampleData.put(data, s); + } + } + + + return Action.SUCCESS.toUpperCase(); + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setMethod(String method) { + this.method = method; + } + + public void setStartEpoch(int startEpoch) { + this.startEpoch = startEpoch; + } + + public void setEndEpoch(int endEpoch) { + this.endEpoch = endEpoch; + } + + public void setTraffic(Map traffic) { + this.traffic = traffic; + } + + public Map getTraffic() { + return this.traffic; + } + + public void setSampleDataList(List sampleDataList) { + this.sampleDataList = sampleDataList; + } + + public List getSampleDataList() { + return this.sampleDataList; + } + + public Map> getSensitiveSampleData() { + return this.sensitiveSampleData; + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/action/UserAction.java b/apps/dashboard/src/main/java/com/akto/action/UserAction.java new file mode 100644 index 0000000000..49e286cc89 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/UserAction.java @@ -0,0 +1,31 @@ +package com.akto.action; + +import com.akto.dto.User; +import com.opensymphony.xwork2.ActionSupport; +import org.apache.struts2.interceptor.SessionAware; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Map; + +public abstract class UserAction extends ActionSupport implements SessionAware { + private User user; + private Map session; + public Map getSession() { + return session; + } + public void setSession(Map session) { + this.session = session; + this.user = (User)(session.get("user")); + } + + public User getSUser() { + return user; + } + + public int today() { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd"); + LocalDateTime now = LocalDateTime.now(); + return Integer.parseInt(dtf.format(now)); + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/ValidateEmailAction.java b/apps/dashboard/src/main/java/com/akto/action/ValidateEmailAction.java new file mode 100644 index 0000000000..1a8eb39e49 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/ValidateEmailAction.java @@ -0,0 +1,40 @@ +package com.akto.action; +import com.akto.dao.UsersDao; +import com.akto.dto.User; +import com.akto.utils.JWT; +import com.opensymphony.xwork2.Action; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +public class ValidateEmailAction implements Action{ + + @Override + public String execute() { + + try { + Jws jws = JWT.parseJwt(emailToken, "/home/avneesh/Desktop/akto/dashboard/public.pem"); + String email= jws.getBody().get("email").toString(); + + User user = UsersDao.validateEmail(email); + + if (user == null) { + return "ERROR"; + } + + } catch (NoSuchAlgorithmException | IOException | InvalidKeySpecException e) { + return "ERROR"; + } + + return "SUCCESS"; + } + + private String emailToken; + + public void setEmailToken(String emailToken) { + this.emailToken = emailToken; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/WebhookAction.java b/apps/dashboard/src/main/java/com/akto/action/WebhookAction.java new file mode 100644 index 0000000000..e97d8d5502 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/WebhookAction.java @@ -0,0 +1,297 @@ +package com.akto.action; + +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +import org.bson.conversions.Bson; + +import com.akto.DaoInit; +import com.akto.dao.context.Context; +import com.akto.dao.notifications.CustomWebhooksDao; +import com.akto.dao.notifications.CustomWebhooksResultDao; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.notifications.CustomWebhook; +import com.akto.dto.notifications.CustomWebhookResult; +import com.akto.dto.notifications.CustomWebhook.ActiveStatus; +import com.akto.dto.type.KeyTypes; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods.Method; +import com.akto.listener.InitializerListener; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.opensymphony.xwork2.Action; + +import com.akto.runtime.Main; +import java.util.concurrent.TimeUnit; + +public class WebhookAction extends UserAction { + + private int id; + private String webhookName; + private String url; + private String headerString; + private String queryParams; + private String body; + private Method method; + private int frequencyInSeconds; + private ActiveStatus activeStatus; + private CustomWebhookResult customWebhookResult; + private List customWebhooks; + + private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + + public String fetchCustomWebhooks() { + customWebhooks = CustomWebhooksDao.instance.findAll(new BasicDBObject()); + return Action.SUCCESS.toUpperCase(); + } + + public String fetchLatestWebhookResult(){ + customWebhookResult = CustomWebhooksResultDao.instance.findLatestOne(Filters.eq("webhookId", id)); + return Action.SUCCESS.toUpperCase(); + } + + public String addCustomWebhook(){ + activeStatus = ActiveStatus.ACTIVE; + + boolean isUrl = KeyTypes.patternToSubType.get(SingleTypeInfo.URL).matcher(url).matches(); + + try{ + OriginalHttpRequest.buildHeadersMap(headerString); + } + catch(Exception e){ + addActionError("Please enter valid headers"); + return ERROR.toUpperCase(); + } + + if (!isUrl) { + addActionError("Please enter a valid url"); + return ERROR.toUpperCase(); + } else if (frequencyInSeconds<=0){ + addActionError("Please enter a valid frequency"); + return ERROR.toUpperCase(); + } else { + int now = Context.now(); + String userEmail = getSUser().getLogin(); + if (userEmail == null) return ERROR.toUpperCase(); + CustomWebhook customWebhook = new CustomWebhook(now,webhookName,url,headerString,queryParams,body,method,frequencyInSeconds,userEmail,now,now,0,activeStatus); + CustomWebhooksDao.instance.insertOne(customWebhook); + fetchCustomWebhooks(); + } + + return Action.SUCCESS.toUpperCase(); + } + + public String updateCustomWebhook(){ + activeStatus = ActiveStatus.ACTIVE; + + CustomWebhook customWebhook = CustomWebhooksDao.instance.findOne( + Filters.eq("_id",id) + ); + boolean isUrl = KeyTypes.patternToSubType.get(SingleTypeInfo.URL).matcher(url).matches(); + + String userEmail = getSUser().getLogin(); + if (userEmail == null) return ERROR.toUpperCase(); + + try{ + OriginalHttpRequest.buildHeadersMap(headerString); + } + catch(Exception e){ + addActionError("Please enter valid headers"); + return ERROR.toUpperCase(); + } + + if (customWebhook == null){ + addActionError("The webhook does not exist"); + return ERROR.toUpperCase(); + } else if ( !userEmail.equals(customWebhook.getUserEmail())){ + addActionError("Unauthorized Request"); + return ERROR.toUpperCase(); + } else if (!isUrl){ + addActionError("Please enter a valid url"); + return ERROR.toUpperCase(); + } else if (frequencyInSeconds<=0){ + addActionError("Please enter a valid frequency"); + return ERROR.toUpperCase(); + } else { + int now = Context.now(); + + Bson updates = + Updates.combine( + Updates.set("url", url), + Updates.set("headerString", headerString), + Updates.set("body", body), + Updates.set("queryParams",queryParams), + Updates.set("method", method), + Updates.set("frequencyInSeconds", frequencyInSeconds), + Updates.set("lastUpdateTime",now), + Updates.set("webhookName", webhookName) + ); + + CustomWebhooksDao.instance.updateOne(Filters.eq("_id",id), updates); + fetchCustomWebhooks(); + } + + return Action.SUCCESS.toUpperCase(); + } + + public String changeStatus(){ + CustomWebhook customWebhook = CustomWebhooksDao.instance.findOne( + Filters.eq("_id",id) + ); + + String userEmail = getSUser().getLogin(); + if (userEmail == null) return ERROR.toUpperCase(); + + if (customWebhook == null){ + addActionError("The webhook does not exist"); + return ERROR.toUpperCase(); + } else if ( !userEmail.equals(customWebhook.getUserEmail() )){ + addActionError("Unauthorized Request"); + return ERROR.toUpperCase(); + } else{ + int now = Context.now(); + + Bson updates = + Updates.combine( + Updates.set("activeStatus",activeStatus), + Updates.set("lastUpdateTime",now) + ); + + CustomWebhooksDao.instance.updateOne(Filters.eq("_id",id), updates); + } + return Action.SUCCESS.toUpperCase(); + } + + public String runOnce(){ + CustomWebhook customWebhook = CustomWebhooksDao.instance.findOne( + Filters.eq("_id",id) + ); + + String userEmail = getSUser().getLogin(); + if (userEmail == null) return ERROR.toUpperCase(); + + if (customWebhook == null){ + addActionError("The webhook does not exist"); + return ERROR.toUpperCase(); + } else if ( !userEmail.equals(customWebhook.getUserEmail() )){ + addActionError("Unauthorized Request"); + return ERROR.toUpperCase(); + } else{ + + int accountId = Context.accountId.get(); + + executorService.schedule( new Runnable() { + public void run() { + Context.accountId.set(accountId); + customWebhook.setFrequencyInSeconds(0); + customWebhook.setLastSentTimestamp(0); + customWebhook.setActiveStatus(ActiveStatus.ACTIVE); + InitializerListener.webhookSenderUtil(customWebhook); + } + }, 1 , TimeUnit.SECONDS); + } + + return Action.SUCCESS.toUpperCase(); + } + + public String deleteCustomWebhook() { + + CustomWebhook customWebhook = CustomWebhooksDao.instance.findOne( + Filters.eq("_id",id) + ); + + String userEmail = getSUser().getLogin(); + if (userEmail == null) return ERROR.toUpperCase(); + + if (!userEmail.equals(customWebhook.getUserEmail())){ + addActionError("Unauthorized Request"); + return ERROR.toUpperCase(); + } else { + CustomWebhooksDao.instance.deleteAll(new BasicDBObject("_id", id)); + } + return Action.SUCCESS.toUpperCase(); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getWebhookName() { + return webhookName; + } + + public void setWebhookName(String webhookName) { + this.webhookName = webhookName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getHeaderString() { + return headerString; + } + + public void setHeaderString(String headerString) { + this.headerString = headerString; + } + + public String getQueryParams() { + return queryParams; + } + + public void setQueryParams(String queryParams) { + this.queryParams = queryParams; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public Method getMethod() { + return method; + } + + public void setMethod(Method method) { + this.method = method; + } + + public int getFrequencyInSeconds() { + return frequencyInSeconds; + } + + public void setFrequencyInSeconds(int frequencyInSeconds) { + this.frequencyInSeconds = frequencyInSeconds; + } + + public ActiveStatus getActiveStatus() { + return activeStatus; + } + + public void setActiveStatus(ActiveStatus activeStatus) { + this.activeStatus = activeStatus; + } + + public CustomWebhookResult getCustomWebhookResult() { + return customWebhookResult; + } + + public List getCustomWebhooks() { + return this.customWebhooks; + } +} \ No newline at end of file diff --git a/apps/dashboard/src/main/java/com/akto/action/misc/OtpAction.java b/apps/dashboard/src/main/java/com/akto/action/misc/OtpAction.java new file mode 100644 index 0000000000..1836d61f8f --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/misc/OtpAction.java @@ -0,0 +1,186 @@ +package com.akto.action.misc; + +import com.akto.action.UserAction; +import com.akto.dao.OtpMessagesDao; +import com.akto.dao.context.Context; +import com.akto.dto.OTPMessage; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.opensymphony.xwork2.Action; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class OtpAction extends UserAction { + + private static final Logger logger = LoggerFactory.getLogger(OtpAction.class); + + private String from; + private String text; + @Override + public String execute() { + Context.accountId.set(1_000_000); + + logger.info(text); + if (text == null || !text.contains("OTP")) { + logger.info("But doesn't contain the word 'OTP' "); + return SUCCESS.toUpperCase(); + } + + logger.info("And contains OTP"); + OTPMessage otpMessage = new OTPMessage(Context.now(), from, text, Context.now()); + OtpMessagesDao.instance.insertOne(otpMessage); + return SUCCESS.toUpperCase(); + } + + private String otp; + public String fetchRecentOtp() { + Context.accountId.set(1_000_000); + List OTPMessageList = OtpMessagesDao.instance.findAll(Filters.gte("timestamp", Context.now() - 90)); + if (OTPMessageList.isEmpty()) return Action.ERROR.toUpperCase(); + + OTPMessage otpMessage = OTPMessageList.get(OTPMessageList.size()-1); // latest + + String val = extractOtp(otpMessage.getMessage()); + if (val == null || val.isEmpty()) return ERROR.toUpperCase(); + + otp = val; + logger.info("found otp: " + otp); + + return SUCCESS.toUpperCase(); + } + + private String extractOtp(String message) { + if (!message.contains("OTP") && !message.contains("otp")) return null; + Pattern pattern = Pattern.compile("(\\d{6})"); + Matcher matcher = pattern.matcher(message); + String val = null; + if (matcher.find()) { + val = matcher.group(0); + } + + return val; + } + + private static final ObjectMapper mapper = new ObjectMapper(); + + private Integer latestMessageId = null; + public String fetchLatestMessageId() { + logger.info(apiKey); + logger.info(authToken); + logger.info(address); + BasicDBObject result; + try { + result = makeRequestToMySms(); + List messages = (List) result.get("messages"); + if (messages.size() == 0) return SUCCESS.toUpperCase(); + + latestMessageId = (Integer) messages.get(0).get("messageId"); + } catch (Exception e) { + return ERROR.toUpperCase(); + } + + return SUCCESS.toUpperCase(); + } + + private Integer lastMessageId; + public String fetchOtpFromMySms() { + try { + BasicDBObject result = makeRequestToMySms(); + + List messages = (List) result.get("messages"); + + Integer messageId = (Integer) messages.get(0).get("messageId"); + if (Objects.equals(messageId, lastMessageId)) return ERROR.toUpperCase(); + + String message = (String) messages.get(0).get("message"); + + String val = extractOtp(message); + if (val == null || val.isEmpty()) return ERROR.toUpperCase(); + + otp = val; + logger.info("found otp: " + otp); + + } catch (Exception e) { + return ERROR.toUpperCase(); + } + + return SUCCESS.toUpperCase(); + } + + private String apiKey ; + private String authToken; + private String address; + private BasicDBObject makeRequestToMySms() throws Exception { + String path = "https://app.mysms.com/json/user/message/get/by/conversation"; + + URL url = new URL(path); + URLConnection con = url.openConnection(); + HttpURLConnection http = (HttpURLConnection)con; + http.setRequestMethod("POST"); // PUT is another valid option + http.setDoOutput(true); + + BasicDBObject req = new BasicDBObject(); + req.put("apiKey", apiKey); + req.put("authToken", authToken); + req.put("address", address); + + String json = req.toJson(); + + http.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + http.connect(); + try(OutputStream os = http.getOutputStream()) { + os.write(json.getBytes(StandardCharsets.UTF_8)); + } + + InputStream inputStream = http.getInputStream(); + + + return mapper.readValue(inputStream, BasicDBObject.class); + } + + public String getOtp() { + return otp; + } + + public void setFrom(String from) { + this.from = from; + } + + public void setText(String text) { + this.text = text; + } + + public void setLastMessageId(Integer lastMessageId) { + this.lastMessageId = lastMessageId; + } + + public Integer getLatestMessageId() { + return latestMessageId; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public void setAuthToken(String authToken) { + this.authToken = authToken; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/observe/InventoryAction.java b/apps/dashboard/src/main/java/com/akto/action/observe/InventoryAction.java new file mode 100644 index 0000000000..a4c22bc8c0 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/observe/InventoryAction.java @@ -0,0 +1,667 @@ +package com.akto.action.observe; + +import com.akto.action.UserAction; +import com.akto.dao.*; +import com.akto.dao.context.Context; +import com.akto.dto.*; +import com.akto.dto.ApiInfo.ApiInfoKey; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods.Method; +import com.akto.util.Constants; +import com.mongodb.BasicDBObject; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.*; +import com.opensymphony.xwork2.Action; +import io.swagger.parser.OpenAPIParser; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.servers.Server; +import io.swagger.v3.parser.core.models.SwaggerParseResult; +import org.bson.conversions.Bson; + +import java.net.URI; +import java.util.*; + +public class InventoryAction extends UserAction { + + int apiCollectionId = -1; + + BasicDBObject response; + + // public String fetchAPICollection() { + // List list = SingleTypeInfoDao.instance.findAll(Filters.eq("apiCollectionId", apiCollectionId)); + // response = new BasicDBObject(); + // response.put("data", new BasicDBObject("name", "Main application").append("endpoints", list)); + + // return Action.SUCCESS.toUpperCase(); + // } + + public final static int DELTA_PERIOD_VALUE = 60 * 24 * 60 * 60; + + public List fetchSensitiveParams() { + Bson filterStandardSensitiveParams = SingleTypeInfoDao.instance.filterForSensitiveParamsExcludingUserMarkedSensitive(apiCollectionId, url, method); + + List list = SingleTypeInfoDao.instance.findAll(filterStandardSensitiveParams); + + return list; + } + + private int startTimestamp = 0; + private int endTimestamp = 0; + + public List fetchRecentEndpoints(int startTimestamp, int endTimestamp) { + List pipeline = new ArrayList<>(); + BasicDBObject groupedId = + new BasicDBObject("apiCollectionId", "$apiCollectionId") + .append("url", "$url") + .append("method", "$method"); + pipeline.add(Aggregates.group(groupedId, Accumulators.min("startTs", "$timestamp"),Accumulators.sum("countTs",1))); + pipeline.add(Aggregates.match(Filters.gte("startTs", startTimestamp))); + pipeline.add(Aggregates.match(Filters.lte("startTs", endTimestamp))); + pipeline.add(Aggregates.sort(Sorts.descending("startTs"))); + MongoCursor endpointsCursor = SingleTypeInfoDao.instance.getMCollection().aggregate(pipeline, BasicDBObject.class).cursor(); + + List endpoints = new ArrayList<>(); + while(endpointsCursor.hasNext()) { + endpoints.add(endpointsCursor.next()); + } + + return endpoints; + } + + public static final int LIMIT = 2000; + public List fetchEndpointsInCollection(int apiCollectionId) { + List pipeline = new ArrayList<>(); + BasicDBObject groupedId = + new BasicDBObject("apiCollectionId", "$apiCollectionId") + .append("url", "$url") + .append("method", "$method"); + + pipeline.add(Aggregates.match(Filters.eq("apiCollectionId", apiCollectionId))); + + int recentEpoch = Context.now() - DELTA_PERIOD_VALUE; + + Bson projections = Projections.fields( + Projections.include("timestamp", "apiCollectionId", "url", "method"), + Projections.computed("dayOfYearFloat", new BasicDBObject("$divide", new Object[]{"$timestamp", recentEpoch})), + Projections.computed("dayOfYear", new BasicDBObject("$trunc", new Object[]{"$dayOfYearFloat", 0})) + ); + + pipeline.add(Aggregates.project(projections)); + pipeline.add(Aggregates.group(groupedId, Accumulators.min("startTs", "$timestamp"), Accumulators.sum("changesCount", 1))); + pipeline.add(Aggregates.skip(skip)); + pipeline.add(Aggregates.limit(LIMIT)); + pipeline.add(Aggregates.sort(Sorts.descending("startTs"))); + + MongoCursor endpointsCursor = SingleTypeInfoDao.instance.getMCollection().aggregate(pipeline, BasicDBObject.class).cursor(); + + List endpoints = new ArrayList<>(); + while(endpointsCursor.hasNext()) { + endpoints.add(endpointsCursor.next()); + } + + return endpoints; + } + + public List fetchEndpointsInCollectionUsingHost(int apiCollectionId) { + + ApiCollection apiCollection = ApiCollectionsDao.instance.getMeta(apiCollectionId); + + if (apiCollection.getHostName() == null || apiCollection.getHostName().length() == 0 ) { + return fetchEndpointsInCollection(apiCollectionId); + } else { + List allUrlsInCollection = fetchHostSTI(apiCollectionId, skip); + + List endpoints = new ArrayList<>(); + for(SingleTypeInfo singleTypeInfo: allUrlsInCollection) { + BasicDBObject groupId = new BasicDBObject("apiCollectionId", singleTypeInfo.getApiCollectionId()) + .append("url", singleTypeInfo.getUrl()) + .append("method", singleTypeInfo.getMethod()); + endpoints.add(new BasicDBObject("startTs", singleTypeInfo.getTimestamp()).append("_id", groupId)); + } + + return endpoints; + } + } + + private String hostName; + private List endpoints; + public String fetchEndpointsBasedOnHostName() { + endpoints = new ArrayList<>(); + if (hostName == null) { + addActionError("Host cannot be null"); + return ERROR.toUpperCase(); + } + + ApiCollection apiCollection = ApiCollectionsDao.instance.findByHost(hostName); + + if (apiCollection == null) { + addActionError("Invalid host"); + return ERROR.toUpperCase(); + } + + List singleTypeInfos = fetchHostSTI(apiCollection.getId(), skip); + for (SingleTypeInfo singleTypeInfo: singleTypeInfos) { + BasicDBObject value = new BasicDBObject(); + value.put("url", singleTypeInfo.getUrl()); + value.put("method", singleTypeInfo.getMethod()); + endpoints.add(value); + } + + return Action.SUCCESS.toUpperCase(); + } + + private Set listOfEndpointsInCollection; + + public String fetchCollectionWiseApiEndpoints() { + listOfEndpointsInCollection = new HashSet<>(); + List list = null; + if (apiCollectionId > -1) { + list = fetchEndpointsInCollectionUsingHost(apiCollectionId); + } + if (list != null && !list.isEmpty()) { + list.forEach(element -> { + BasicDBObject item = (BasicDBObject) element.get(Constants.ID); + if (item == null) { + return; + } + ApiInfoKey apiInfoKey = new ApiInfoKey( + apiCollectionId, + item.getString(ApiInfoKey.URL), + Method.fromString(item.getString(ApiInfoKey.METHOD))); + listOfEndpointsInCollection.add(apiInfoKey); + }); + } + return SUCCESS.toUpperCase(); + } + + + public static List fetchHostSTI(int apiCollectionId, int skip) { + Bson filterQ = SingleTypeInfoDao.filterForHostHeader(apiCollectionId, true); + return SingleTypeInfoDao.instance.findAll(filterQ, skip,10_000, null); + } + + + private void attachTagsInAPIList(List list) { + List tagConfigs = TagConfigsDao.instance.findAll(new BasicDBObject("active", true)); + for (BasicDBObject singleTypeInfo: list) { + singleTypeInfo = (BasicDBObject) (singleTypeInfo.getOrDefault("_id", new BasicDBObject())); + String url = singleTypeInfo.getString("url"); + List tags = new ArrayList<>(); + for(TagConfig tagConfig: tagConfigs) { + if (tagConfig.getKeyConditions().validate(url)) { + tags.add(tagConfig.getName()); + } + } + singleTypeInfo.put("tags", tags); + } + } + + private void attachAPIInfoListInResponse(List list, int apiCollectionId) { + response = new BasicDBObject(); + List apiInfoList = new ArrayList<>(); + + Set apiInfoKeys = new HashSet(); + for (BasicDBObject singleTypeInfo: list) { + singleTypeInfo = (BasicDBObject) (singleTypeInfo.getOrDefault("_id", new BasicDBObject())); + apiInfoKeys.add(new ApiInfoKey(singleTypeInfo.getInt("apiCollectionId"),singleTypeInfo.getString("url"), Method.fromString(singleTypeInfo.getString("method")))); + } + + BasicDBObject query = new BasicDBObject(); + if (apiCollectionId > -1) { + query.append("_id.apiCollectionId", apiCollectionId); + } + + int counter = 0; + int batchSize = 100; + + List urlsToSearch = new ArrayList<>(); + + for(ApiInfoKey apiInfoKey: apiInfoKeys) { + urlsToSearch.add(apiInfoKey.getUrl()); + counter++; + if (counter % batchSize == 0 || counter == apiInfoKeys.size()) { + query.append("_id.url", new BasicDBObject("$in", urlsToSearch)); + List fromDb = ApiInfoDao.instance.findAll(query); + for (ApiInfo a: fromDb) { + if (apiInfoKeys.contains(a.getId())) { + a.calculateActualAuth(); + apiInfoList.add(a); + } + } + urlsToSearch.clear(); + } + } + + + response.put("data", new BasicDBObject("endpoints", list).append("apiInfoList", apiInfoList)); + } + + public static String retrievePath(String url) { + URI uri = URI.create(url); + String prependPath = uri.getPath(); + if (!prependPath.startsWith("/")) prependPath = "/" + prependPath; + if (prependPath.endsWith("/")) prependPath = prependPath.substring(0, prependPath.length()-1); + return prependPath; + } + + public static Set fetchSwaggerData(List endpoints, OpenAPI openAPI) { + List servers = openAPI.getServers(); + + List prependPaths = new ArrayList<>(); + if (servers == null) { + servers = new ArrayList<>(); + servers.add(new Server().url("/")); + } + + for (Server server: servers) { + String url = server.getUrl(); + String prependPath = retrievePath(url); + prependPaths.add(prependPath); + } + + Paths paths = openAPI.getPaths(); + + Set strictPathsSet = new HashSet<>(); + Map templatePathsMap = new HashMap<>(); + Set unused = new HashSet<>(); + + for(String path: paths.keySet()) { + PathItem pathItem = paths.get(path); + if (pathItem == null) continue; + + if (path.contains("{")) { + for (String prependPath: prependPaths) { + String finalUrl = prependPath + path; + templatePathsMap.put(finalUrl, pathItem); + for (PathItem.HttpMethod pathMethod: pathItem.readOperationsMap().keySet()) { + unused.add(finalUrl + " " + pathMethod); + } + } + continue; + } + + for (PathItem.HttpMethod operationType: pathItem.readOperationsMap().keySet()) { + String method = operationType.toString().toUpperCase(); + for (String prependPath: prependPaths) { + String finalUrl = prependPath + path; + strictPathsSet.add(finalUrl + " " + method); + unused.add(finalUrl + " " + method); + } + } + } + + for (BasicDBObject endpoint: endpoints) { + String endpointUrl = (String) ((BasicDBObject) endpoint.get("_id")).get("url"); + // clean endpoint + String path = retrievePath(endpointUrl); + String method = (String) ((BasicDBObject) endpoint.get("_id")).get("method"); + if (!path.startsWith("/")) path = "/" + path; + // match with strict + String endpointKey = path + " " + method; + if (strictPathsSet.contains(endpointKey)) { + unused.remove(endpointKey); + continue; + } + + String[] r = path.split("/"); + boolean matched = false; + // if not then loop over templates + for (String p: templatePathsMap.keySet()) { + if (matched) break; + // check if method exists + Operation operation = templatePathsMap.get(p).readOperationsMap().get(PathItem.HttpMethod.valueOf(method)); + if (operation == null) continue; + + // check if same length + String[] q = p.split("/"); + if (q.length != r.length) continue; + + // loop over + boolean flag = true; + for (int i =0; i < q.length; i ++) { + if (Objects.equals(q[i], r[i]) ) continue; + if ((Objects.equals(r[i], "STRING") || Objects.equals(r[i], "INTEGER")) && q[i].contains("{")) continue; + + flag = false; + break; + } + + if (flag) { + unused.remove(p + " " + method); + matched = true; + } + + } + + if (!matched) { + endpoint.append("shadow",true); + } + } + + + return unused; + } + + public String fetchAPICollection() { + List list = fetchEndpointsInCollectionUsingHost(apiCollectionId); + + APISpec apiSpec = APISpecDao.instance.findById(apiCollectionId); + Set unused = null; + try { + if (apiSpec != null) { + SwaggerParseResult result = new OpenAPIParser().readContents(apiSpec.getContent(), null, null); + OpenAPI openAPI = result.getOpenAPI(); + unused = fetchSwaggerData(list, openAPI); + } + } catch (Exception e) { + ; + } + + attachTagsInAPIList(list); + attachAPIInfoListInResponse(list, apiCollectionId); + + if (unused == null) { + unused = new HashSet<>(); + } + response.put("unusedEndpoints", unused); + + return Action.SUCCESS.toUpperCase(); + } + + public String loadRecentEndpoints() { + List list = fetchRecentEndpoints(startTimestamp, endTimestamp); + attachTagsInAPIList(list); + attachAPIInfoListInResponse(list, -1); + return Action.SUCCESS.toUpperCase(); + } + + public String loadSensitiveParameters() { + + List list = fetchSensitiveParams(); + List filterCustomSensitiveParams = new ArrayList<>(); + + filterCustomSensitiveParams.add(Filters.eq("sensitive", true)); + + if (apiCollectionId >= 0) { + Bson apiCollectionIdFilter = Filters.eq("apiCollectionId", apiCollectionId); + + filterCustomSensitiveParams.add(apiCollectionIdFilter); + } + + if (url != null) { + Bson urlFilter = Filters.eq("url", url); + + filterCustomSensitiveParams.add(urlFilter); + } + + if (method != null) { + Bson methodFilter = Filters.eq("method", method); + + filterCustomSensitiveParams.add(methodFilter); + + } + + List customSensitiveList = SensitiveParamInfoDao.instance.findAll(Filters.and(filterCustomSensitiveParams)); + + list.addAll(customSensitiveList); + + response = new BasicDBObject(); + response.put("data", new BasicDBObject("endpoints", list)); + + return Action.SUCCESS.toUpperCase(); + } + + public String fetchNewParametersTrend() { + List pipeline = new ArrayList<>(); + pipeline.add(Aggregates.match(Filters.gte("timestamp", startTimestamp))); + pipeline.add(Aggregates.match(Filters.lte("timestamp", endTimestamp))); + pipeline.add(Aggregates.project(Projections.computed("dayOfYearFloat", new BasicDBObject("$divide", new Object[]{"$timestamp", 86400})))); + pipeline.add(Aggregates.project(Projections.computed("dayOfYear", new BasicDBObject("$trunc", new Object[]{"$dayOfYearFloat", 0})))); + pipeline.add(Aggregates.group("$dayOfYear", Accumulators.sum("count", 1))); + + MongoCursor endpointsCursor = SingleTypeInfoDao.instance.getMCollection().aggregate(pipeline, BasicDBObject.class).cursor(); + + List endpoints = new ArrayList<>(); + while(endpointsCursor.hasNext()) { + endpoints.add(endpointsCursor.next()); + } + + response = new BasicDBObject(); + response.put("data", new BasicDBObject("endpoints", endpoints)); + + return Action.SUCCESS.toUpperCase(); + + } + + public List fetchAllNewParams(int startTimestamp,int endTimestamp){ + Bson filterNewParams = SingleTypeInfoDao.instance.filterForAllNewParams(startTimestamp,endTimestamp); + + List list = SingleTypeInfoDao.instance.findAll(filterNewParams); + + return list; + } + + public String fetchAllUrlsAndMethods() { + response = new BasicDBObject(); + BasicDBObject ret = new BasicDBObject(); + + APISpec apiSpec = APISpecDao.instance.findById(apiCollectionId); + if (apiSpec != null) { + SwaggerParseResult result = new OpenAPIParser().readContents(apiSpec.getContent(), null, null); + OpenAPI openAPI = result.getOpenAPI(); + Paths paths = openAPI.getPaths(); + for(String path: paths.keySet()) { + ret.append(path, paths.get(path).readOperationsMap().keySet()); + } + } + + response.put("data", ret); + + return Action.SUCCESS.toUpperCase(); + } + + private String sortKey; + private int sortOrder; + private int limit; + private int skip; + private Map filters; + private Map filterOperators; + + private Bson prepareFilters() { + ArrayList filterList = new ArrayList<>(); + filterList.add(Filters.gt("timestamp", startTimestamp)); + filterList.add(Filters.lt("timestamp", endTimestamp)); + for(Map.Entry entry: filters.entrySet()) { + String key = entry.getKey(); + List value = entry.getValue(); + + if (value.size() == 0) continue; + String operator = filterOperators.get(key); + + switch (key) { + case "color": continue; + case "url": + case "param": + switch (operator) { + case "OR": + case "AND": + filterList.add(Filters.regex(key, ".*"+value.get(0)+".*", "i")); + break; + case "NOT": + filterList.add(Filters.not(Filters.regex(key, ".*"+value.get(0)+".*", "i"))); + break; + } + + break; + case "timestamp": + List ll = value; + filterList.add(Filters.lte(key, (long) (Context.now()) - ll.get(0) * 86400L)); + filterList.add(Filters.gte(key, (long) (Context.now()) - ll.get(1) * 86400L)); + break; + default: + switch (operator) { + case "OR": + case "AND": + filterList.add(Filters.in(key, value)); + break; + + case "NOT": + filterList.add(Filters.nin(key, value)); + break; + } + + } + } + + return Filters.and(filterList); + + } + + + public String url; + public String method; + public String loadParamsOfEndpoint() { + Bson filters = Filters.and( + Filters.eq("apiCollectionId", apiCollectionId), + Filters.eq("url", url), + Filters.eq("method", method) + ); + + List list = SingleTypeInfoDao.instance.findAll(filters); + + response = new BasicDBObject(); + response.put("data", new BasicDBObject("params", list)); + return Action.SUCCESS.toUpperCase(); + } + + private List getMongoResults() { + + List sortFields = new ArrayList<>(); + sortFields.add(sortKey); + + Bson sort = sortOrder == 1 ? Sorts.ascending(sortFields) : Sorts.descending(sortFields); + + List list = SingleTypeInfoDao.instance.findAll(Filters.and(prepareFilters()), skip, limit, sort); + return list; + } + + private long getTotalParams() { + return SingleTypeInfoDao.instance.getMCollection().countDocuments(prepareFilters()); + } + + public String fetchChanges() { + response = new BasicDBObject(); + response.put("data", new BasicDBObject("endpoints", getMongoResults()).append("total", getTotalParams())); + + return Action.SUCCESS.toUpperCase(); + } + + public String getSortKey() { + return this.sortKey; + } + + public void setSortKey(String sortKey) { + this.sortKey = sortKey; + } + + public int getSortOrder() { + return this.sortOrder; + } + + public void setSortOrder(int sortOrder) { + this.sortOrder = sortOrder; + } + + public int getLimit() { + return this.limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public int getSkip() { + return this.skip; + } + + public void setSkip(int skip) { + this.skip = skip; + } + + public int getApiCollectionId() { + return this.apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public BasicDBObject getResponse() { + return this.response; + } + + public void setResponse(BasicDBObject response) { + this.response = response; + } + + public Map getFilters() { + return this.filters; + } + + public void setFilters(Map filters) { + this.filters = filters; + } + + public Map getFilterOperators() { + return this.filterOperators; + } + + public void setFilterOperators(Map filterOperators) { + this.filterOperators = filterOperators; + } + + public String getUrl() { + return this.url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getMethod() { + return this.method; + } + + public void setMethod(String method) { + this.method = method; + } + + public void setStartTimestamp(int startTimestamp) { + this.startTimestamp = startTimestamp; + } + + public void setEndTimestamp(int endTimestamp) { + this.endTimestamp = endTimestamp; + } + + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + + public List getEndpoints() { + return endpoints; + } + + public Set getListOfEndpointsInCollection() { + return listOfEndpointsInCollection; + } + + public void setListOfEndpointsInCollection(Set listOfEndpointsInCollection) { + this.listOfEndpointsInCollection = listOfEndpointsInCollection; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/quick_start/QuickStartAction.java b/apps/dashboard/src/main/java/com/akto/action/quick_start/QuickStartAction.java new file mode 100644 index 0000000000..16a58faeae --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/quick_start/QuickStartAction.java @@ -0,0 +1,347 @@ +package com.akto.action.quick_start; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.*; +import java.util.concurrent.*; + +import com.akto.util.Constants; +import com.akto.utils.DashboardMode; +import com.akto.utils.platform.DashboardStackDetails; +import com.akto.utils.platform.MirroringStackDetails; +import com.akto.utils.cloud.stack.dto.StackState; +import com.amazonaws.services.cloudformation.model.Tag; +import org.apache.commons.lang3.StringUtils; +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.akto.action.UserAction; +import com.akto.dao.ApiTokensDao; +import com.akto.dao.AwsResourcesDao; +import com.akto.dao.BackwardCompatibilityDao; +import com.akto.dao.context.Context; +import com.akto.dto.AwsResources; +import com.akto.dto.BackwardCompatibility; +import com.akto.dto.User; +import com.akto.dto.third_party_access.PostmanCredential; +import com.akto.utils.cloud.CloudType; +import com.akto.utils.cloud.Utils; +import com.akto.utils.cloud.serverless.UpdateFunctionRequest; +import com.akto.utils.cloud.serverless.aws.Lambda; +import com.akto.utils.cloud.stack.Stack; +import com.akto.utils.cloud.stack.aws.AwsStack; +import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancing; +import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancingClientBuilder; +import com.amazonaws.services.elasticloadbalancingv2.model.DescribeLoadBalancersRequest; +import com.amazonaws.services.elasticloadbalancingv2.model.DescribeLoadBalancersResult; +import com.amazonaws.services.elasticloadbalancingv2.model.LoadBalancer; +import com.akto.dto.ApiToken; +import com.akto.dto.AwsResource; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.opensymphony.xwork2.Action; +public class QuickStartAction extends UserAction { + + private boolean dashboardHasNecessaryRole; + private List availableLBs; + private List selectedLBs; + private boolean isFirstSetup; + private StackState stackState; + private List configuredItems; + private String awsRegion; + private String awsAccountId; + private String aktoDashboardRoleName; + private String aktoMirroringStackName; + + private String aktoDashboardStackName; + + + private static final Logger logger = LoggerFactory.getLogger(QuickStartAction.class); + + public String fetchQuickStartPageState() { + + configuredItems = new ArrayList<>(); + + // Fetching cloud integration + if(DashboardMode.isOnPremDeployment()) { + CloudType type = Utils.getCloudType(); + configuredItems.add(type.toString()); + } + + // Looking if burp is integrated or not + ApiToken burpToken = ApiTokensDao.instance.findOne(Filters.eq(ApiToken.UTILITY, ApiToken.Utility.BURP)); + if (burpToken != null) { + configuredItems.add(ApiToken.Utility.BURP.toString()); + } + User u = getSUser(); + PostmanCredential postmanCredential = com.akto.utils.Utils.fetchPostmanCredential(u.getId()); + if(postmanCredential != null) { + configuredItems.add("POSTMAN"); + } + return Action.SUCCESS.toUpperCase(); + } + + public String fetchLoadBalancers() { + List availableLBs = new ArrayList<>(); + List selectedLBs = new ArrayList<>(); + ExecutorService executorService = Executors.newFixedThreadPool(3); + try { + Future dashboardLBNameFuture = executorService.submit(()-> AwsStack.getInstance().fetchResourcePhysicalIdByLogicalId(DashboardStackDetails.getStackName(), DashboardStackDetails.AKTO_LB_DASHBOARD)); + Future aktoNLBNameFuture = executorService.submit(()-> AwsStack.getInstance().fetchResourcePhysicalIdByLogicalId(MirroringStackDetails.getStackName(), MirroringStackDetails.AKTO_NLB)); + AmazonElasticLoadBalancing amazonElasticLoadBalancingClient = AmazonElasticLoadBalancingClientBuilder + .defaultClient(); + Future loadBalanersFuture = executorService.submit(() -> amazonElasticLoadBalancingClient + .describeLoadBalancers(new DescribeLoadBalancersRequest())); + + + DescribeLoadBalancersResult result = loadBalanersFuture.get(); + String dashboardLBName = filterLBName(dashboardLBNameFuture.get()); + String aktoNLBName = filterLBName(aktoNLBNameFuture.get()); + executorService.shutdown(); + Map lbInfo = new HashMap<>(); + for (LoadBalancer lb : result.getLoadBalancers()) { + String lbName = lb.getLoadBalancerName().toLowerCase(); + if ( lbName.contains("akto") || lbName.equalsIgnoreCase(dashboardLBName) || lbName.equalsIgnoreCase(aktoNLBName)) { + continue; + } + lbInfo.put(lb.getLoadBalancerArn(), new AwsResource(lb.getLoadBalancerName(), lb.getLoadBalancerArn())); + } + this.dashboardHasNecessaryRole = true; + availableLBs = new ArrayList<>(lbInfo.values()); + AwsResources resources = AwsResourcesDao.instance.findOne(AwsResourcesDao.generateFilter()); + if (resources != null && resources.getLoadBalancers() != null) { + for (AwsResource selectedLb : resources.getLoadBalancers()) { + if (lbInfo.containsKey(selectedLb.getResourceId())) { + selectedLBs.add(lbInfo.get(selectedLb.getResourceId())); + } + } + } + } catch (Exception e) { + logger.error("Error occurred while fetching LBs", e); + this.dashboardHasNecessaryRole = false; + } + this.awsRegion = System.getenv(Constants.AWS_REGION); + this.awsAccountId = System.getenv(Constants.AWS_ACCOUNT_ID); + this.selectedLBs = selectedLBs; + this.availableLBs = availableLBs; + this.aktoDashboardRoleName = DashboardStackDetails.getAktoDashboardRole(); + this.aktoMirroringStackName = MirroringStackDetails.getStackName(); + this.aktoDashboardStackName = DashboardStackDetails.getStackName(); + return Action.SUCCESS.toUpperCase(); + } + + private String filterLBName(String lbArn) { + if(StringUtils.isEmpty(lbArn)){ + return ""; + } + String[] details = lbArn.split(":"); + String lastDetail = details[details.length-1]; + String[] resourceNameDetails = lastDetail.split("/"); + if(resourceNameDetails.length <= 1){ + return ""; + } + return resourceNameDetails[resourceNameDetails.length-2]; + } + + public String saveLoadBalancers() { + Bson updates = Updates.set("loadBalancers", this.selectedLBs); + AwsResourcesDao.instance.updateOne(Filters.eq("_id", Context.accountId.get()), updates); + if (!AwsStack.getInstance().checkIfStackExists(MirroringStackDetails.getStackName())) { + this.isFirstSetup = true; + try { + Map parameters = new HashMap() { + { + put("MongoIp", System.getenv("AKTO_MONGO_CONN")); + put("KeyPair", System.getenv("EC2_KEY_PAIR")); + put("SourceLBs", extractLBs()); + put("SubnetId", System.getenv("EC2_SUBNET_ID")); + } + }; + String template = convertStreamToString(AwsStack.class + .getResourceAsStream("/cloud_formation_templates/akto_aws_mirroring.template")); + List tags = Utils.fetchTags(DashboardStackDetails.getStackName()); + String stackId = AwsStack.getInstance().createStack(MirroringStackDetails.getStackName(), parameters, template, tags); + } catch (Exception e) { + ; + } + + } else { + this.isFirstSetup = false; + try { + Map updatedEnvVars = new HashMap() { + { + put("ELB_NAMES", extractLBs()); + } + }; + String functionName = AwsStack.getInstance().fetchResourcePhysicalIdByLogicalId(MirroringStackDetails.getStackName(), MirroringStackDetails.CREATE_MIRROR_SESSION_LAMBDA); + UpdateFunctionRequest ufr = new UpdateFunctionRequest(updatedEnvVars); + Lambda.getInstance().updateFunctionConfiguration(functionName, ufr); + // invoke lambda + } catch (Exception e) { + ; + } + } + this.stackState = AwsStack.getInstance().fetchStackStatus(MirroringStackDetails.getStackName()); + fetchLoadBalancers(); + return Action.SUCCESS.toUpperCase(); + } + + public String extractLBs() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < this.selectedLBs.size(); i++) { + sb.append(this.selectedLBs.get(i).getResourceName()); + if (i != this.selectedLBs.size() - 1) { + sb.append(","); + } + } + return sb.toString(); + } + + public String checkStackCreationProgress() { + this.stackState = AwsStack.getInstance().fetchStackStatus(MirroringStackDetails.getStackName()); + invokeLambdaIfNecessary(stackState); + if(Stack.StackStatus.CREATION_FAILED.toString().equalsIgnoreCase(this.stackState.getStatus())){ + AwsResourcesDao.instance.getMCollection().deleteOne(Filters.eq("_id", Context.accountId.get())); + logger.info("Current stack status is failed, so we are removing entry from db"); + } + if(Stack.StackStatus.DOES_NOT_EXISTS.toString().equalsIgnoreCase(this.stackState.getStatus())){ + AwsResources resources = AwsResourcesDao.instance.findOne(AwsResourcesDao.generateFilter()); + if(resources != null && resources.getLoadBalancers().size() > 0){ + AwsResourcesDao.instance.getMCollection().deleteOne(AwsResourcesDao.generateFilter()); + logger.info("Stack does not exists but entry present in DB, removing it"); + fetchLoadBalancers(); + } else { + logger.info("Nothing set in DB, moving on"); + } + } + return Action.SUCCESS.toUpperCase(); + } + + private void invokeLambdaIfNecessary(StackState stackState){ + Runnable runnable = () -> { + if(Stack.StackStatus.CREATE_COMPLETE.toString().equals(this.stackState.getStatus())){ + Context.accountId.set(1_000_000); + BackwardCompatibility backwardCompatibility = BackwardCompatibilityDao.instance.findOne(new BasicDBObject()); + if(!backwardCompatibility.isMirroringLambdaTriggered()){ + try{ + String functionName = AwsStack.getInstance().fetchResourcePhysicalIdByLogicalId(MirroringStackDetails.getStackName(), MirroringStackDetails.CREATE_MIRROR_SESSION_LAMBDA); + Lambda.getInstance().invokeFunction(functionName); + BackwardCompatibilityDao.instance.updateOne( + Filters.eq("_id", backwardCompatibility.getId()), + Updates.set(BackwardCompatibility.MIRRORING_LAMBDA_TRIGGERED, true) + ); + logger.info("Successfully triggered CreateMirrorSession"); + } catch(Exception e){ + logger.error("Failed to invoke lambda for the first time", e); + } + } else { + logger.info("Already invoked"); + } + } + }; + new Thread(runnable).start(); + } + + public boolean getDashboardHasNecessaryRole() { + return dashboardHasNecessaryRole; + } + + public void setDashboardHasNecessaryRole(boolean dashboardHasNecessaryRole) { + this.dashboardHasNecessaryRole = dashboardHasNecessaryRole; + } + + public String getAwsRegion() { + return awsRegion; + } + + public void setAwsRegion(String awsRegion) { + this.awsRegion = awsRegion; + } + + public String getAwsAccountId() { + return awsAccountId; + } + + public void setAwsAccountId(String awsAccountId) { + this.awsAccountId = awsAccountId; + } + + public boolean getIsFirstSetup() { + return dashboardHasNecessaryRole; + } + + public void setIsFirstSetup(boolean isFirstSetup) { + this.isFirstSetup = isFirstSetup; + } + + public List getAvailableLBs() { + return availableLBs; + } + + public void setAvailableLBs(List availableLBs) { + this.availableLBs = availableLBs; + } + + public List getSelectedLBs() { + return selectedLBs; + } + + public void setSelectedLBs(List selectedLBs) { + this.selectedLBs = selectedLBs; + } + + public StackState getStackState() { + return stackState; + } + + public void setStackState(StackState stackState) { + this.stackState = stackState; + } + + public List getConfiguredItems() { + return configuredItems; + } + + public void setConfiguredItems(List configuredItems) { + this.configuredItems = configuredItems; + } + + public String getAktoDashboardRoleName() { + return aktoDashboardRoleName; + } + + public void setAktoDashboardRoleName(String aktoDashboardRoleName) { + this.aktoDashboardRoleName = aktoDashboardRoleName; + } + + public String getAktoMirroringStackName() { + return aktoMirroringStackName; + } + + public void setAktoMirroringStackName(String aktoMirroringStackName) { + this.aktoMirroringStackName = aktoMirroringStackName; + } + + public String getAktoDashboardStackName() { + return aktoDashboardStackName; + } + + public void setAktoDashboardStackName(String aktoDashboardStackName) { + this.aktoDashboardStackName = aktoDashboardStackName; + } + // Convert a stream into a single, newline separated string + private static String convertStreamToString(InputStream in) throws Exception { + + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + StringBuilder stringbuilder = new StringBuilder(); + String line = null; + while ((line = reader.readLine()) != null) { + stringbuilder.append(line + "\n"); + } + in.close(); + return stringbuilder.toString(); + } +} \ No newline at end of file diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/AuthMechanismAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/AuthMechanismAction.java new file mode 100644 index 0000000000..33a665f7a4 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/testing/AuthMechanismAction.java @@ -0,0 +1,223 @@ +package com.akto.action.testing; + +import com.akto.action.UserAction; +import com.akto.dao.AuthMechanismsDao; +import com.akto.dao.testing.LoginFlowStepsDao; +import com.akto.dao.testing.TestingRunDao; +import com.akto.dao.testing.WorkflowTestResultsDao; +import com.akto.dto.testing.*; +import com.akto.log.LoggerMaker; +import com.akto.testing.TestExecutor; +import com.akto.util.enums.LoginFlowEnums; +import com.google.gson.Gson; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class AuthMechanismAction extends UserAction { + + private ArrayList requestData; + + private String type; + + private AuthMechanism authMechanism; + + private ArrayList authParamData; + + private String uuid; + + private ArrayList responses; + + private String nodeId; + + private static final LoggerMaker loggerMaker = new LoggerMaker(AuthMechanismAction.class); + + public String addAuthMechanism() { + List authParams = new ArrayList<>(); + + type = type != null ? type : LoginFlowEnums.AuthMechanismTypes.HARDCODED.toString(); + + AuthMechanismsDao.instance.deleteAll(new BasicDBObject()); + + if (type.equals(LoginFlowEnums.AuthMechanismTypes.HARDCODED.toString())) { + if (authParamData != null) { + if (!authParamData.get(0).validate()) { + addActionError("Key, Value, Location can't be empty"); + return ERROR.toUpperCase(); + } + authParams.add(new HardcodedAuthParam(authParamData.get(0).getWhere(), authParamData.get(0).getKey(), + authParamData.get(0).getValue(), true)); + } + + } else { + + for (AuthParamData param: authParamData) { + if (!param.validate()) { + addActionError("Key, Value, Location can't be empty"); + return ERROR.toUpperCase(); + } + + authParams.add(new LoginRequestAuthParam(param.getWhere(), param.getKey(), param.getValue(), param.getShowHeader())); + } + } + AuthMechanism authMechanism = new AuthMechanism(authParams, requestData, type); + + AuthMechanismsDao.instance.insertOne(authMechanism); + return SUCCESS.toUpperCase(); + } + + public String triggerLoginFlowSteps() { + List authParams = new ArrayList<>(); + + if (type.equals(LoginFlowEnums.AuthMechanismTypes.HARDCODED.toString())) { + addActionError("Invalid Type Value"); + return ERROR.toUpperCase(); + } + + for (AuthParamData param: authParamData) { + if (!param.validate()) { + addActionError("Key, Value, Location can't be empty"); + return ERROR.toUpperCase(); + } + authParams.add(new LoginRequestAuthParam(param.getWhere(), param.getKey(), param.getValue(), param.getShowHeader())); + } + + AuthMechanism authMechanism = new AuthMechanism(authParams, requestData, type); + + TestExecutor testExecutor = new TestExecutor(); + try { + LoginFlowResponse loginFlowResponse = testExecutor.executeLoginFlow(authMechanism, null); + responses = loginFlowResponse.getResponses(); + if (!loginFlowResponse.getSuccess()) { + throw new Exception(loginFlowResponse.getError()); + } + } catch(Exception e) { + addActionError(e.getMessage()); + return ERROR.toUpperCase(); + } + return SUCCESS.toUpperCase(); + } + + public String triggerSingleLoginFlowStep() { + List authParams = new ArrayList<>(); + + if (type.equals(LoginFlowEnums.AuthMechanismTypes.HARDCODED.toString())) { + addActionError("Invalid Type Value"); + return ERROR.toUpperCase(); + } + + AuthMechanism authMechanism = new AuthMechanism(authParams, requestData, type); + + TestExecutor testExecutor = new TestExecutor(); + try { + LoginFlowParams loginFlowParams = new LoginFlowParams(getSUser().getId(), true, nodeId); + LoginFlowResponse loginFlowResponse = testExecutor.executeLoginFlow(authMechanism, loginFlowParams); + responses = loginFlowResponse.getResponses(); + if (!loginFlowResponse.getSuccess()) { + throw new Exception(loginFlowResponse.getError()); + } + } catch(Exception e) { + addActionError(e.getMessage()); + return ERROR.toUpperCase(); + } + return SUCCESS.toUpperCase(); + } + + public String fetchAuthMechanismData() { + + authMechanism = AuthMechanismsDao.instance.findOne(new BasicDBObject()); + return SUCCESS.toUpperCase(); + } + + private int workflowTestId; + private WorkflowTestResult workflowTestResult; + private TestingRun workflowTestingRun; + public String fetchWorkflowResult() { + workflowTestingRun = TestingRunDao.instance.findLatestOne( + Filters.eq(TestingRun._TESTING_ENDPOINTS+"."+WorkflowTestingEndpoints._WORK_FLOW_TEST+"._id", workflowTestId) + ); + + if (workflowTestingRun == null) { + return SUCCESS.toUpperCase(); + } + + workflowTestResult = WorkflowTestResultsDao.instance.findOne( + Filters.eq(WorkflowTestResult._TEST_RUN_ID, workflowTestingRun.getId()) + ); + + return SUCCESS.toUpperCase(); + } + + public String getType() { + return this.type; + } + + + public ArrayList getRequestData() { + return this.requestData; + } + + public AuthMechanism getAuthMechanism() { + return this.authMechanism; + } + + public ArrayList getAuthParamData() { + return this.authParamData; + } + + public String getUuid() { + return this.uuid; + } + + public String getNodeId() { + return this.nodeId; + } + + public ArrayList getResponses() { + return this.responses; + } + + + public void setWorkflowTestId(int workflowTestId) { + this.workflowTestId = workflowTestId; + } + + public WorkflowTestResult getWorkflowTestResult() { + return workflowTestResult; + } + + public TestingRun getWorkflowTestingRun() { + return workflowTestingRun; + } + + public void setType(String type) { + this.type = type; + } + + public void setRequestData(ArrayList requestData) { + this.requestData = requestData; + } + + public void setAuthParamData(ArrayList authParamData) { + this.authParamData = authParamData; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/LoginRecorderAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/LoginRecorderAction.java new file mode 100644 index 0000000000..68e2b861b0 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/testing/LoginRecorderAction.java @@ -0,0 +1,116 @@ +package com.akto.action.testing; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.io.File; + +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.akto.action.UserAction; +import com.akto.dao.RecordedLoginInputDao; +import com.akto.dao.context.Context; +import com.akto.dao.testing.LoginFlowStepsDao; +import com.akto.dto.RecordedLoginFlowInput; +import com.akto.util.RecordedLoginFlowUtil; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; + + +public class LoginRecorderAction extends UserAction { + + private String content; + + private String tokenFetchCommand; + + private String token; + + private String nodeId; + + private static final Logger logger = LoggerFactory.getLogger(LoginRecorderAction.class); + + private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + + public String uploadRecordedFlow() { + + String payload = this.content.toString(); + + int accountId = Context.accountId.get(); + + executorService.schedule( new Runnable() { + public void run() { + try { + Context.accountId.set(accountId); + File tmpOutputFile = File.createTempFile("output", ".json"); + File tmpErrorFile = File.createTempFile("recordedFlowOutput", ".txt"); + RecordedLoginFlowUtil.triggerFlow(tokenFetchCommand, payload, tmpOutputFile.getPath(), tmpErrorFile.getPath(), getSUser().getId()); + } catch (Exception e) { + logger.error("error running recorded flow " + e.getMessage()); + } + } + }, 1, TimeUnit.SECONDS); + + return SUCCESS.toUpperCase(); + } + + public String fetchRecordedFlowOutput() { + + RecordedLoginFlowInput recordedLoginInput = RecordedLoginInputDao.instance.findOne(Filters.eq("userId", getSUser().getId())); + + try { + token = RecordedLoginFlowUtil.fetchToken(recordedLoginInput.getOutputFilePath(), recordedLoginInput.getErrorFilePath()); + } catch(Exception e) { + addActionError(e.getMessage()); + return ERROR.toUpperCase(); + } + + Map valuesMap = new HashMap<>(); + valuesMap.put(nodeId + ".response.body.token", token); + + Integer userId = getSUser().getId(); + + Bson filter = Filters.and( + Filters.eq("userId", userId) + ); + Bson update = Updates.set("valuesMap", valuesMap); + LoginFlowStepsDao.instance.updateOne(filter, update); + + return SUCCESS.toUpperCase(); + } + + public String getContent() { + return this.content; + } + + public String getNodeId() { + return this.nodeId; + } + + public String getTokenFetchCommand() { + return this.tokenFetchCommand; + } + public String getToken() { + return this.token; + } + + public void setContent(String content) { + this.content = content; + } + + public void setTokenFetchCommand(String tokenFetchCommand) { + this.tokenFetchCommand = tokenFetchCommand; + } + + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } + + public void setToken(String token) { + this.token = token; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/MarketplaceAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/MarketplaceAction.java new file mode 100644 index 0000000000..41d5c14ced --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/testing/MarketplaceAction.java @@ -0,0 +1,112 @@ +package com.akto.action.testing; + +import java.util.List; + +import com.akto.action.UserAction; +import com.akto.dao.context.Context; +import com.akto.dao.testing.sources.TestSourceConfigsDao; +import com.akto.dto.testing.sources.TestSourceConfig; +import com.akto.util.enums.GlobalEnums.Severity; +import com.akto.util.enums.GlobalEnums.TestCategory; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.opensymphony.xwork2.Action; + +import org.bson.conversions.Bson; + +public class MarketplaceAction extends UserAction { + + List testSourceConfigs; + public String fetchAllMarketplaceSubcategories() { + this.testSourceConfigs = TestSourceConfigsDao.instance.findAll(new BasicDBObject()); + return Action.SUCCESS.toUpperCase(); + } + + + boolean defaultCreator; + String subcategory; + public String fetchTestingSources() { + Bson creatorQ = Filters.ne(TestSourceConfig.CREATOR, "default"); + Bson subcategoryQ = Filters.eq(TestSourceConfig.SUBCATEGORY, subcategory); + Bson filterQ = defaultCreator ? subcategoryQ : Filters.and(creatorQ, subcategoryQ); + + this.testSourceConfigs = TestSourceConfigsDao.instance.findAll(filterQ); + return Action.SUCCESS.toUpperCase(); + } + + String url; + TestCategory category; + Severity severity; + String description; + public String addCustomTest() { + TestSourceConfig alreadyExists = TestSourceConfigsDao.instance.findOne("_id", url); + if (alreadyExists != null) { + addActionError("This test file has already been added"); + return ERROR.toUpperCase(); + } + + TestSourceConfig elem = new TestSourceConfig(url, category, subcategory, severity, description, getSUser().getLogin(), Context.now()); + TestSourceConfigsDao.instance.insertOne(elem); + return Action.SUCCESS.toUpperCase(); + } + + public boolean isDefaultCreator() { + return this.defaultCreator; + } + + public boolean getDefaultCreator() { + return this.defaultCreator; + } + + public void setDefaultCreator(boolean defaultCreator) { + this.defaultCreator = defaultCreator; + } + + public String getSubcategory() { + return this.subcategory; + } + + public void setSubcategory(String subcategory) { + this.subcategory = subcategory; + } + + public List getTestSourceConfigs() { + return this.testSourceConfigs; + } + + public void setTestSourceConfigs(List testSourceConfigs) { + this.testSourceConfigs = testSourceConfigs; + } + + public String getUrl() { + return this.url; + } + + public void setUrl(String url) { + this.url = url; + } + + public TestCategory getCategory() { + return this.category; + } + + public void setCategory(TestCategory category) { + this.category = category; + } + + public Severity getSeverity() { + return this.severity; + } + + public void setSeverity(Severity severity) { + this.severity = severity; + } + + public String getDescription() { + return this.description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/OtpTestDataAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/OtpTestDataAction.java new file mode 100644 index 0000000000..6a1176e936 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/testing/OtpTestDataAction.java @@ -0,0 +1,106 @@ +package com.akto.action.testing; + +import com.akto.action.UserAction; +import com.akto.dao.OtpTestDataDao; +import com.akto.dao.context.Context; +import com.akto.dto.testing.OtpTestData; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class OtpTestDataAction extends UserAction { + + private String regex; + + private String uuid; + + private String otpText; + + private String otp; + + private static final Logger logger = LoggerFactory.getLogger(OtpTestDataAction.class); + + public String saveOtpData() { + + if (otpText == null || uuid == null) { + addActionError("otpText, uuid cannot be null"); + return ERROR.toUpperCase(); + } + + int curTime = Context.now(); + Bson updates = Updates.combine( + Updates.set("otpText", otpText), + Updates.set("createdAtEpoch", curTime) + ); + + OtpTestDataDao.instance.updateOne(Filters.eq("uuid", uuid), updates); + return SUCCESS.toUpperCase(); + } + + public String fetchOtpData() { + + if (uuid == null) { + addActionError("uuid cannot be null"); + return ERROR.toUpperCase(); + } + + int timeFilterVal = Context.now() - 5 * 60; + + Bson filters = Filters.and( + Filters.eq("uuid", uuid), + Filters.gte("createdAtEpoch", timeFilterVal) + ); + OtpTestData otpTestData = null; + try { + otpTestData = OtpTestDataDao.instance.findOne(filters); + if (otpTestData == null) { + addActionError("otp data not received for uuid " + uuid); + return ERROR.toUpperCase(); + } + } catch(Exception e) { + addActionError("Error fetching otp data for uuid " + uuid + " error " + e.getMessage()); + return ERROR.toUpperCase(); + } + + otpText = otpTestData.getOtpText(); + + return SUCCESS.toUpperCase(); + } + + public String getRegex() { + return this.regex; + } + + public String getUuid() { + return this.uuid; + } + + public String getOtpText() { + return this.otpText; + } + + public String getOtp() { + return this.otp; + } + + public void setRegex(String regex) { + this.regex = regex; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setOtpText(String otpText) { + this.otpText = otpText; + } + + public void setOtp(String otp) { + this.otp = otp; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java new file mode 100644 index 0000000000..dba2f0c9bf --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -0,0 +1,368 @@ +package com.akto.action.testing; + +import com.akto.action.UserAction; +import com.akto.dao.AuthMechanismsDao; +import com.akto.dao.context.Context; +import com.akto.dao.testing.TestingRunDao; +import com.akto.dao.testing.TestingRunResultDao; +import com.akto.dao.testing.TestingRunResultSummariesDao; +import com.akto.dao.testing.WorkflowTestsDao; +import com.akto.dao.testing.sources.TestSourceConfigsDao; +import com.akto.dao.testing_run_findings.TestingRunIssuesDao; +import com.akto.dao.testing.*; +import com.akto.dto.ApiInfo; +import com.akto.dto.User; +import com.akto.dto.test_run_findings.TestingIssuesId; +import com.akto.dto.test_run_findings.TestingRunIssues; +import com.akto.dto.testing.*; +import com.akto.dto.testing.TestingRun.State; +import com.akto.dto.testing.sources.TestSourceConfig; +import com.akto.util.Constants; +import com.akto.util.enums.GlobalEnums; +import com.akto.util.enums.GlobalEnums.TestErrorSource; +import com.akto.util.enums.GlobalEnums.TestSubCategory; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Sorts; +import com.mongodb.client.model.Updates; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; + +import java.util.List; + +public class StartTestAction extends UserAction { + + private TestingEndpoints.Type type; + private int apiCollectionId; + private List apiInfoKeyList; + private int testIdConfig; + private int workflowTestId; + private int startTimestamp; + private int testRunTime; + private int maxConcurrentRequests; + boolean recurringDaily; + private List testingRuns; + private AuthMechanism authMechanism; + private int endTimestamp; + private String testName; + + private TestingRun createTestingRun(int scheduleTimestamp, int periodInSeconds) { + User user = getSUser(); + + AuthMechanism authMechanism = AuthMechanismsDao.instance.findOne(new BasicDBObject()); + if (authMechanism == null && testIdConfig == 0) { + addActionError("Please set authentication mechanism before you test any APIs"); + return null; + } + + TestingEndpoints testingEndpoints; + switch (type) { + case CUSTOM: + if (this.apiInfoKeyList == null || this.apiInfoKeyList.isEmpty()) { + addActionError("APIs list can't be empty"); + return null; + } + testingEndpoints = new CustomTestingEndpoints(apiInfoKeyList); + break; + case COLLECTION_WISE: + testingEndpoints = new CollectionWiseTestingEndpoints(apiCollectionId); + break; + case WORKFLOW: + WorkflowTest workflowTest = WorkflowTestsDao.instance.findOne(Filters.eq("_id", this.workflowTestId)); + if (workflowTest == null) { + addActionError("Couldn't find workflow test"); + return null; + } + testingEndpoints = new WorkflowTestingEndpoints(workflowTest); + testIdConfig = 1; + break; + default: + addActionError("Invalid APIs type"); + return null; + } + if (this.selectedTests != null) { + TestingRunConfig testingRunConfig = new TestingRunConfig(Context.now(), null, this.selectedTests,authMechanism.getId()); + this.testIdConfig = testingRunConfig.getId(); + TestingRunConfigDao.instance.insertOne(testingRunConfig); + } + + return new TestingRun(scheduleTimestamp, user.getLogin(), + testingEndpoints, testIdConfig, State.SCHEDULED, periodInSeconds, testName, this.testRunTime, this.maxConcurrentRequests); + } + private List selectedTests; + + public String startTest() { + int scheduleTimestamp = this.startTimestamp == 0 ? Context.now() : this.startTimestamp; + + TestingRun testingRun = createTestingRun(scheduleTimestamp, this.recurringDaily ? 86400 : 0); + + if (testingRun == null) { + return ERROR.toUpperCase(); + } else { + TestingRunDao.instance.insertOne(testingRun); + } + + this.startTimestamp = 0; + this.endTimestamp = 0; + this.retrieveAllCollectionTests(); + return SUCCESS.toUpperCase(); + } + + public String retrieveAllCollectionTests() { + if (this.startTimestamp == 0) { + this.startTimestamp = Context.now(); + } + + if (this.endTimestamp == 0) { + this.endTimestamp = Context.now() + 86400; + } + + this.authMechanism = AuthMechanismsDao.instance.findOne(new BasicDBObject()); + + Bson filterQ = Filters.and( + Filters.lte(TestingRun.SCHEDULE_TIMESTAMP, this.endTimestamp), + Filters.gte(TestingRun.SCHEDULE_TIMESTAMP, this.startTimestamp) + ); + + testingRuns = TestingRunDao.instance.findAll(filterQ); + testingRuns.sort((o1, o2) -> o2.getScheduleTimestamp() - o1.getScheduleTimestamp()); + return SUCCESS.toUpperCase(); + } + + String testingRunHexId; + List testingRunResultSummaries; + final int limitForTestingRunResultSummary = 20; + private TestingRun testingRun; + private WorkflowTest workflowTest; + public String fetchTestingRunResultSummaries() { + ObjectId testingRunId; + try { + testingRunId = new ObjectId(testingRunHexId); + } catch (Exception e) { + addActionError("Invalid test id"); + return ERROR.toUpperCase(); + } + + Bson filterQ = Filters.eq(TestingRunResultSummary.TESTING_RUN_ID, testingRunId); + + Bson sort = Sorts.descending(TestingRunResultSummary.START_TIMESTAMP) ; + + this.testingRunResultSummaries = TestingRunResultSummariesDao.instance.findAll(filterQ, 0, limitForTestingRunResultSummary , sort); + this.testingRun = TestingRunDao.instance.findOne(Filters.eq("_id", testingRunId)); + + if (this.testingRun.getTestIdConfig() == 1) { + WorkflowTestingEndpoints workflowTestingEndpoints = (WorkflowTestingEndpoints) testingRun.getTestingEndpoints(); + this.workflowTest = WorkflowTestsDao.instance.findOne(Filters.eq("_id", workflowTestingEndpoints.getWorkflowTest().getId())); + } + + return SUCCESS.toUpperCase(); + } + + String testingRunResultSummaryHexId; + List testingRunResults; + public String fetchTestingRunResults() { + ObjectId testingRunResultSummaryId; + try { + testingRunResultSummaryId= new ObjectId(testingRunResultSummaryHexId); + } catch (Exception e) { + addActionError("Invalid test summary id"); + return ERROR.toUpperCase(); + } + + this.testingRunResults = TestingRunResultDao.instance.fetchLatestTestingRunResult( testingRunResultSummaryId); + + return SUCCESS.toUpperCase(); + } + + private String testingRunResultHexId; + private TestingRunResult testingRunResult; + public String fetchTestRunResultDetails() { + ObjectId testingRunResultId = new ObjectId(testingRunResultHexId); + this.testingRunResult = TestingRunResultDao.instance.findOne("_id", testingRunResultId); + return SUCCESS.toUpperCase(); + } + + private TestingRunIssues runIssues; + public String fetchIssueFromTestRunResultDetails() { + ObjectId testingRunResultId = new ObjectId(testingRunResultHexId); + TestingRunResult result = TestingRunResultDao.instance.findOne(Constants.ID, testingRunResultId); + try { + if (result.isVulnerable()) { + TestSubCategory category = TestSubCategory.getTestCategory(result.getTestSubType()); + TestSourceConfig config = null; + if (category == null) { + config = TestSourceConfigsDao.instance.getTestSourceConfig(result.getTestSubType()); + } + TestingIssuesId issuesId = new TestingIssuesId(result.getApiInfoKey(), TestErrorSource.AUTOMATED_TESTING, + category, config != null ? config.getId() : null); + runIssues = TestingRunIssuesDao.instance.findOne(Filters.eq(Constants.ID, issuesId)); + } + }catch (Exception ignore) {} + + return SUCCESS.toUpperCase(); + } + + public String fetchWorkflowTestingRun() { + Bson filterQ = Filters.and( + Filters.eq("testingEndpoints.workflowTest._id", workflowTestId), + Filters.eq("state", TestingRun.State.SCHEDULED) + ); + this.testingRuns = TestingRunDao.instance.findAll(filterQ); + return SUCCESS.toUpperCase(); + } + + public String deleteScheduledWorkflowTests() { + Bson filter = Filters.and( + Filters.or( + Filters.eq(TestingRun.STATE, State.SCHEDULED), + Filters.eq(TestingRun.STATE, State.RUNNING) + ), + Filters.eq("testingEndpoints.workflowTest._id", workflowTestId) + ); + Bson update = Updates.set(TestingRun.STATE, State.STOPPED); + + TestingRunDao.instance.getMCollection().updateMany(filter, update); + + return SUCCESS.toUpperCase(); + } + + public String stopAllTests() { + // stop all the scheduled and running tests + Bson filter = Filters.or( + Filters.eq(TestingRun.STATE, State.SCHEDULED), + Filters.eq(TestingRun.STATE, State.RUNNING) + ); + + TestingRunDao.instance.getMCollection().updateMany(filter,Updates.set(TestingRun.STATE, State.STOPPED)); + testingRuns = TestingRunDao.instance.findAll(filter); + + return SUCCESS.toUpperCase(); + } + + public void setType(TestingEndpoints.Type type) { + this.type = type; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public void setApiInfoKeyList(List apiInfoKeyList) { + this.apiInfoKeyList = apiInfoKeyList; + } + + public List getTestingRuns() { + return testingRuns; + } + + public AuthMechanism getAuthMechanism() { + return this.authMechanism; + } + + public void setAuthMechanism(AuthMechanism authMechanism) { + this.authMechanism = authMechanism; + } + + public int getStartTimestamp() { + return this.startTimestamp; + } + + public void setStartTimestamp(int startTimestamp) { + this.startTimestamp = startTimestamp; + } + + public int getEndTimestamp() { + return this.endTimestamp; + } + + public void setEndTimestamp(int endTimestamp) { + this.endTimestamp = endTimestamp; + } + + public boolean getRecurringDaily() { + return this.recurringDaily; + } + + public void setRecurringDaily(boolean recurringDaily) { + this.recurringDaily = recurringDaily; + } + + public void setTestIdConfig(int testIdConfig) { + this.testIdConfig = testIdConfig; + } + + public void setWorkflowTestId(int workflowTestId) { + this.workflowTestId = workflowTestId; + } + + public void setTestingRunHexId(String testingRunHexId) { + this.testingRunHexId = testingRunHexId; + } + + public List getTestingRunResultSummaries() { + return this.testingRunResultSummaries; + } + + public void setTestingRunResultSummaryHexId(String testingRunResultSummaryHexId) { + this.testingRunResultSummaryHexId = testingRunResultSummaryHexId; + } + + public List getTestingRunResults() { + return this.testingRunResults; + } + + public void setTestingRunResultHexId(String testingRunResultHexId) { + this.testingRunResultHexId = testingRunResultHexId; + } + + public TestingRunResult getTestingRunResult() { + return testingRunResult; + } + + public TestingRun getTestingRun() { + return testingRun; + } + + public WorkflowTest getWorkflowTest() { + return workflowTest; + } + + public TestingRunIssues getRunIssues() { + return runIssues; + } + + public void setRunIssues(TestingRunIssues runIssues) { + this.runIssues = runIssues; + } + public List getSelectedTests() { + return selectedTests; + } + + public void setSelectedTests(List selectedTests) { + this.selectedTests = selectedTests; + } + + public String getTestName() { + return this.testName; + } + + public void setTestName(String testName) { + this.testName = testName; + } + + public int getTestRunTime() { + return testRunTime; + } + + public void setTestRunTime(int testRunTime) { + this.testRunTime = testRunTime; + } + + public int getMaxConcurrentRequests() { + return maxConcurrentRequests; + } + + public void setMaxConcurrentRequests(int maxConcurrentRequests) { + this.maxConcurrentRequests = maxConcurrentRequests; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/TestRolesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/TestRolesAction.java new file mode 100644 index 0000000000..bd05d10c1a --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/testing/TestRolesAction.java @@ -0,0 +1,180 @@ +package com.akto.action.testing; + +import com.akto.action.UserAction; +import com.akto.dao.context.Context; +import com.akto.dao.testing.EndpointLogicalGroupDao; +import com.akto.dao.testing.TestRolesDao; +import com.akto.dto.data_types.Conditions; +import com.akto.dto.data_types.Conditions.Operator; +import com.akto.dto.data_types.Predicate; +import com.akto.dto.data_types.Predicate.Type; +import com.akto.dto.testing.EndpointLogicalGroup; +import com.akto.dto.testing.TestRoles; +import com.akto.util.Constants; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; + +import java.util.ArrayList; +import java.util.List; + +public class TestRolesAction extends UserAction { + private List testRoles; + private TestRoles selectedRole; + private RolesConditionUtils andConditions; + private RolesConditionUtils orConditions; + private String roleName; + + public static class RolesConditionUtils { + private Operator operator; + private List predicates; + public RolesConditionUtils(){} + /* + *This constructor is empty because struts 2 needs empty constructors to make objects and assign values via getters and setters + * */ + public Operator getOperator() { + return operator; + } + + public void setOperator(Operator operator) { + this.operator = operator; + } + + public List getPredicates() { + return predicates; + } + + public void setPredicates(List predicates) { + this.predicates = predicates; + } + } + + public String fetchAllRolesAndLogicalGroups() { + testRoles = TestRolesDao.instance.findAll(Filters.empty()); + List endpointLogicalGroups = EndpointLogicalGroupDao.instance.findAll(Filters.empty()); + + testRoles.forEach((item) -> { + for (int index = 0; index < endpointLogicalGroups.size(); index++) { + if (endpointLogicalGroups.get(index).getId().equals(item.getEndpointLogicalGroupId())) { + item.setEndpointLogicalGroup(endpointLogicalGroups.get(index)); + break; + } + } + }); + return SUCCESS.toUpperCase(); + } + + public String updateTestRoles() { + if (roleName == null) { + addActionError("Test role id is empty"); + return ERROR.toUpperCase(); + } + + TestRoles role = TestRolesDao.instance.findOne(Filters.eq(TestRoles.NAME, roleName)); + if (role == null) {//Role doesn't exists + addActionError("Role doesn't exists"); + return ERROR.toUpperCase(); + } + + Conditions orConditions = null; + if (this.orConditions != null) { + orConditions = new Conditions(); + orConditions.setOperator(this.orConditions.getOperator()); + orConditions.setPredicates(getPredicatesFromPredicatesObject(this.orConditions.getPredicates())); + } + Conditions andConditions = null; + if (this.andConditions != null) { + andConditions = new Conditions(); + andConditions.setOperator(this.andConditions.getOperator()); + andConditions.setPredicates(getPredicatesFromPredicatesObject(this.andConditions.getPredicates())); + } + //Valid role name and regex + EndpointLogicalGroup logicalGroup = EndpointLogicalGroupDao.instance.findOne(Filters.eq(Constants.ID, role.getEndpointLogicalGroupId())); + + if (logicalGroup != null) { + EndpointLogicalGroupDao.instance.updateLogicalGroup(logicalGroup, andConditions, orConditions); + } + role.setLastUpdatedTs(Context.now()); + TestRolesDao.instance.updateOne(Filters.eq(Constants.ID, role.getId()), Updates.set(TestRoles.LAST_UPDATED_TS, Context.now())); + return SUCCESS.toUpperCase(); + } + public String createTestRole () { + if (roleName == null || roleName.isEmpty()) { + addActionError("Test role name is empty"); + return ERROR.toUpperCase(); + } + + if (TestRolesDao.instance.getMCollection().countDocuments(Filters.eq(TestRoles.NAME, roleName)) > 0) {//Role exists + addActionError("Role already exists"); + return ERROR.toUpperCase(); + } + + Conditions orConditions = null; + if (this.orConditions != null) { + orConditions = new Conditions(); + orConditions.setOperator(this.orConditions.getOperator()); + orConditions.setPredicates(getPredicatesFromPredicatesObject(this.orConditions.getPredicates())); + } + Conditions andConditions = null; + if (this.andConditions != null) { + andConditions = new Conditions(); + andConditions.setOperator(this.andConditions.getOperator()); + andConditions.setPredicates(getPredicatesFromPredicatesObject(this.andConditions.getPredicates())); + } + //Valid role name and regex + String logicalGroupName = roleName + EndpointLogicalGroup.GROUP_NAME_SUFFIX; + EndpointLogicalGroup logicalGroup = EndpointLogicalGroupDao.instance. + createLogicalGroup(logicalGroupName, andConditions,orConditions,this.getSUser().getLogin()); + selectedRole = TestRolesDao.instance.createTestRole(roleName, logicalGroup.getId(), this.getSUser().getLogin()); + selectedRole.setEndpointLogicalGroup(logicalGroup); + return SUCCESS.toUpperCase(); + } + + private List getPredicatesFromPredicatesObject(List predicates) { + List arrayList = new ArrayList<>(); + for (int index = 0; index < predicates.size(); index++) { + Type type = Type.valueOf(predicates.get(index).getString(Predicate.TYPE)); + Predicate predicate = Predicate.generatePredicate(type, predicates.get(index)); + arrayList.add(predicate); + } + return arrayList; + } + + public List getTestRoles() { + return testRoles; + } + + public void setTestRoles(List testRoles) { + this.testRoles = testRoles; + } + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + public RolesConditionUtils getAndConditions() { + return andConditions; + } + + public void setAndConditions(RolesConditionUtils andConditions) { + this.andConditions = andConditions; + } + + public RolesConditionUtils getOrConditions() { + return orConditions; + } + + public void setOrConditions(RolesConditionUtils orConditions) { + this.orConditions = orConditions; + } + + public TestRoles getSelectedRole() { + return selectedRole; + } + + public void setSelectedRole(TestRoles selectedRole) { + this.selectedRole = selectedRole; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/WorkflowTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/WorkflowTestAction.java new file mode 100644 index 0000000000..d8832c2d66 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/testing/WorkflowTestAction.java @@ -0,0 +1,260 @@ +package com.akto.action.testing; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.akto.action.UserAction; +import com.akto.dao.context.Context; +import com.akto.dao.testing.WorkflowTestsDao; +import com.akto.dto.testing.WorkflowNodeDetails; +import com.akto.dto.testing.WorkflowTest; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Updates; +import com.mongodb.client.result.InsertOneResult; + +import org.bson.conversions.Bson; + +public class WorkflowTestAction extends UserAction { + int apiCollectionId; + private List nodes; + private List edges; + private Map mapNodeIdToWorkflowNodeDetails; + WorkflowTest.State state; + int id; + String str; + private List workflowTests; + + public String fetchWorkflowTests() { + workflowTests = WorkflowTestsDao.instance.findAll(new BasicDBObject()); + return SUCCESS.toUpperCase(); + } + + public String createWorkflowTest() { + int id = Context.now(); + String author = getSUser().getLogin(); + int createdTimestamp = id; + String editor = author; + int lastEdited = createdTimestamp; + + if (mapNodeIdToWorkflowNodeDetails == null) { + addActionError("nodes can't be null"); + return ERROR.toUpperCase(); + } + + for (WorkflowNodeDetails workflowNodeDetails: mapNodeIdToWorkflowNodeDetails.values()) { + String err = workflowNodeDetails.validate(); + if (err != null) { + addActionError(err); + return ERROR.toUpperCase(); + } + } + + WorkflowTest workflowTest = new WorkflowTest(id, apiCollectionId, author, createdTimestamp, editor, + lastEdited, nodes, edges, mapNodeIdToWorkflowNodeDetails, state); + + InsertOneResult result = WorkflowTestsDao.instance.insertOne(workflowTest); + int newId = result.getInsertedId().asInt32().intValue(); + workflowTests = new ArrayList(); + workflowTests.add(WorkflowTestsDao.instance.findOne("_id", newId)); + + return SUCCESS.toUpperCase(); + } + + public String editWorkflowTest() { + WorkflowTest workflowTest = findWorkflowTest(id); + if (workflowTest == null) { + return ERROR.toUpperCase(); + } + + Bson updates = Updates.combine( + Updates.set("nodes", nodes), + Updates.set("edges", edges), + Updates.set("mapNodeIdToWorkflowNodeDetails", mapNodeIdToWorkflowNodeDetails) + ); + + updateWorkflowTest(id, updates); + + return SUCCESS.toUpperCase(); + } + + public String setWorkflowTestState() { + WorkflowTest workflowTest = findWorkflowTest(id); + if (workflowTest == null) { + return ERROR.toUpperCase(); + } + + updateWorkflowTest(id, Updates.set("state", state)); + + return SUCCESS.toUpperCase(); + } + + public String exportWorkflowTestAsString() { + WorkflowTest workflowTest = findWorkflowTest(id); + if (workflowTest == null) { + return ERROR.toUpperCase(); + } + + ObjectMapper objectMapper = new ObjectMapper(); + try { + str = objectMapper.writeValueAsString(workflowTest); + } catch (JsonProcessingException e) { + return ERROR.toUpperCase(); + } + return SUCCESS.toUpperCase(); + } + + public String editWorkflowNodeDetails() { + WorkflowTest workflowTest = findWorkflowTest(id); + if (workflowTest == null || mapNodeIdToWorkflowNodeDetails.size() != 1) { + return ERROR.toUpperCase(); + } + + String nodeId = mapNodeIdToWorkflowNodeDetails.keySet().iterator().next(); + Map m = workflowTest.getMapNodeIdToWorkflowNodeDetails(); + if (m == null || m.get(nodeId) == null) { + return ERROR.toUpperCase(); + } + + WorkflowNodeDetails details = mapNodeIdToWorkflowNodeDetails.get(nodeId); + Bson update = Updates.set("mapNodeIdToWorkflowNodeDetails."+nodeId, details); + updateWorkflowTest(id, update); + + return SUCCESS.toUpperCase(); + } + + private WorkflowTest findWorkflowTest(int workflowTestId) { + WorkflowTest workflowTest = WorkflowTestsDao.instance.findOne("_id", workflowTestId); + return workflowTest; + } + + private void updateWorkflowTest(int workflowTestId, Bson updates) { + String editor = getSUser().getLogin(); + int lastEdited = Context.now(); + Bson allUpdates = Updates.combine( + Updates.set("editor", editor), + Updates.set("lastEdited", lastEdited), + updates + ); + WorkflowTestsDao.instance.updateOne("_id", id, allUpdates); + workflowTests = new ArrayList(); + workflowTests.add(WorkflowTestsDao.instance.findOne("_id", id)); + } + + private String workflowTestJson; + public String downloadWorkflowAsJson() { + WorkflowTest workflowTest = findWorkflowTest(id); + if (workflowTest == null) { + addActionError("Invalid workflow test"); + return ERROR.toUpperCase(); + } + + workflowTestJson = new Gson().toJson(workflowTest); + + return SUCCESS.toUpperCase(); + } + + public String uploadWorkflowJson() { + WorkflowTest workflowTest = new Gson().fromJson(workflowTestJson,WorkflowTest.class); + + if (workflowTest == null) { + addActionError("Invalid workflow test json"); + return ERROR.toUpperCase(); + } + + int id = Context.now(); + String author = getSUser().getLogin(); + int createdTimestamp = id; + String editor = author; + int lastEdited = createdTimestamp; + + workflowTest.setId(id); + workflowTest.setAuthor(author); + workflowTest.setCreatedTimestamp(createdTimestamp); + workflowTest.setEditor(author); + workflowTest.setLastEdited(lastEdited); + workflowTest.setApiCollectionId(apiCollectionId); + + WorkflowTestsDao.instance.insertOne(workflowTest); + + workflowTests = new ArrayList<>(); + workflowTests.add(workflowTest); + + return SUCCESS.toUpperCase(); + } + + public int getApiCollectionId() { + return this.apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public List getNodes() { + return this.nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public List getEdges() { + return this.edges; + } + + public void setEdges(List edges) { + this.edges = edges; + } + + public Map getMapNodeIdToWorkflowNodeDetails() { + return this.mapNodeIdToWorkflowNodeDetails; + } + + public void setMapNodeIdToWorkflowNodeDetails(Map mapNodeIdToWorkflowNodeDetails) { + this.mapNodeIdToWorkflowNodeDetails = mapNodeIdToWorkflowNodeDetails; + } + + public WorkflowTest.State getState() { + return this.state; + } + + public void setState(WorkflowTest.State state) { + this.state = state; + } + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + public List getWorkflowTests() { + return this.workflowTests; + } + + public void setWorkflowTests(List workflowTests) { + this.workflowTests = workflowTests; + } + + public String getStr() { + return this.str; + } + + public void setStr(String str) { + this.str = str; + } + + public String getWorkflowTestJson() { + return workflowTestJson; + } + + public void setWorkflowTestJson(String workflowTestJson) { + this.workflowTestJson = workflowTestJson; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java new file mode 100644 index 0000000000..c0ddd8e230 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java @@ -0,0 +1,315 @@ +package com.akto.action.testing_issues; + +import com.akto.action.UserAction; +import com.akto.dao.testing.TestingRunResultDao; +import com.akto.dao.testing.sources.TestSourceConfigsDao; +import com.akto.dao.testing_run_findings.TestingRunIssuesDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.test_run_findings.TestingIssuesId; +import com.akto.dto.test_run_findings.TestingRunIssues; +import com.akto.dto.testing.TestingRunResult; +import com.akto.dto.testing.sources.TestSourceConfig; +import com.akto.util.enums.GlobalEnums; +import com.akto.util.enums.GlobalEnums.TestSubCategory; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Sorts; +import com.mongodb.client.model.Updates; +import org.bouncycastle.util.test.Test; +import org.bson.conversions.Bson; + +import java.util.ArrayList; +import java.util.List; + +import static com.akto.util.Constants.ID; +import static com.akto.util.enums.GlobalEnums.*; + +public class IssuesAction extends UserAction { + private List issues; + private TestingIssuesId issueId; + private List issueIdArray; + private TestingRunResult testingRunResult; + private TestRunIssueStatus statusToBeUpdated; + private String ignoreReason; + private int skip; + private int limit; + private long totalIssuesCount; + private List filterStatus; + private List filterCollectionsId; + private List filterSeverity; + private List filterSubCategory; + private List similarlyAffectedIssues; + private int startEpoch; + private Bson createFilters () { + Bson filters = Filters.empty(); + if (filterStatus != null && !filterStatus.isEmpty()) { + filters = Filters.and(filters, Filters.in(TestingRunIssues.TEST_RUN_ISSUES_STATUS, filterStatus)); + } + if (filterCollectionsId != null && !filterCollectionsId.isEmpty()) { + filters = Filters.and(filters, Filters.in(ID + "." + + TestingIssuesId.API_KEY_INFO + "." + + ApiInfo.ApiInfoKey.API_COLLECTION_ID, filterCollectionsId)); + } + if (filterSeverity != null && !filterSeverity.isEmpty()) { + filters = Filters.and(filters, Filters.in(TestingRunIssues.KEY_SEVERITY, filterSeverity)); + } + if (filterSubCategory != null && !filterSubCategory.isEmpty()) { + filters = Filters.and(filters, Filters.in(ID + "." + + TestingIssuesId.TEST_SUB_CATEGORY, filterSubCategory)); + } + if (startEpoch != 0) { + filters = Filters.and(filters, Filters.gte(TestingRunIssues.CREATION_TIME, startEpoch)); + } + return filters; + } + + public String fetchAffectedEndpoints() { + Bson sort = Sorts.orderBy(Sorts.descending(TestingRunIssues.TEST_RUN_ISSUES_STATUS), + Sorts.descending(TestingRunIssues.CREATION_TIME)); + TestSubCategory subCategory = issueId.getTestSubCategory(); + TestCategory superCategory; + if (subCategory == null) { + superCategory = TestSourceConfigsDao.instance.getTestSourceConfig(issueId.getTestCategoryFromSourceConfig()).getCategory(); + } else { + superCategory = issueId.getTestSubCategory().getSuperCategory(); + } + + List subCategoryList = new ArrayList<>(); + for (TestSubCategory sctg : TestSubCategory.getValuesArray()) { + if (sctg.getSuperCategory() == superCategory) { + subCategoryList.add(sctg); + } + } + List sourceConfigs = TestSourceConfigsDao.instance.findAll(Filters.empty()); + List sourceConfigIds = new ArrayList<>(); + for (TestSourceConfig sourceConfig : sourceConfigs) { + sourceConfigIds.add(sourceConfig.getId()); + } + Bson filters = Filters.and( + Filters.or( + Filters.in(ID + "." + TestingIssuesId.TEST_SUB_CATEGORY, subCategoryList), + Filters.in(ID + "." + TestingIssuesId.TEST_CATEGORY_FROM_SOURCE_CONFIG, sourceConfigIds) + ), Filters.ne(ID, issueId)); + similarlyAffectedIssues = TestingRunIssuesDao.instance.findAll(filters, 0,3, sort); + return SUCCESS.toUpperCase(); + } + + public String fetchAllIssues() { + Bson sort = Sorts.orderBy(Sorts.descending(TestingRunIssues.TEST_RUN_ISSUES_STATUS), + Sorts.descending(TestingRunIssues.CREATION_TIME)); + Bson filters = createFilters(); + totalIssuesCount = TestingRunIssuesDao.instance.getMCollection().countDocuments(filters); + issues = TestingRunIssuesDao.instance.findAll(filters, skip,limit, sort); + + for (TestingRunIssues runIssue : issues) { + if (runIssue.getId().getTestSubCategory() == null) {//TestSourceConfig case + TestSourceConfig config = TestSourceConfigsDao.instance.getTestSourceConfig(runIssue.getId().getTestCategoryFromSourceConfig()); + runIssue.getId().setTestSourceConfig(config); + } + } + return SUCCESS.toUpperCase(); + } + public String fetchTestingRunResult() { + if (issueId == null) { + throw new IllegalStateException(); + } + TestingRunIssues issue = TestingRunIssuesDao.instance.findOne(Filters.eq(ID, issueId)); + String testSubType = null; + if (issue.getId().getTestSubCategory() == null) { + testSubType = issue.getId().getTestCategoryFromSourceConfig(); + } else { + testSubType = issue.getId().getTestSubCategory().getName(); + } + Bson filterForRunResult = Filters.and( + Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, issue.getLatestTestingRunSummaryId()), + Filters.eq(TestingRunResult.TEST_SUB_TYPE, testSubType), + Filters.eq(TestingRunResult.API_INFO_KEY, issue.getId().getApiInfoKey()) + ); + testingRunResult = TestingRunResultDao.instance.findOne(filterForRunResult); + return SUCCESS.toUpperCase(); + } + + private TestSubCategory[] subCategories; + private TestCategory[] categories; + private List testSourceConfigs; + public String fetchAllSubCategories() { + this.subCategories = GlobalEnums.TestSubCategory.getValuesArray(); + this.categories = GlobalEnums.TestCategory.values(); + this.testSourceConfigs = TestSourceConfigsDao.instance.findAll(Filters.empty()); + return SUCCESS.toUpperCase(); + } + + + public String updateIssueStatus () { + if (issueId == null || statusToBeUpdated == null || ignoreReason == null) { + throw new IllegalStateException(); + } + + Bson update = Updates.set(TestingRunIssues.TEST_RUN_ISSUES_STATUS, statusToBeUpdated); + + if (statusToBeUpdated == TestRunIssueStatus.IGNORED) { //Changing status to ignored + update = Updates.combine(update, Updates.set(TestingRunIssues.IGNORE_REASON, ignoreReason)); + } else { + update = Updates.combine(update, Updates.unset(TestingRunIssues.IGNORE_REASON)); + } + TestingRunIssues updatedIssue = TestingRunIssuesDao.instance.updateOne(Filters.eq(ID, issueId), update); + issueId = updatedIssue.getId(); + ignoreReason = updatedIssue.getIgnoreReason(); + statusToBeUpdated = updatedIssue.getTestRunIssueStatus(); + return SUCCESS.toUpperCase(); + } + + public String bulkUpdateIssueStatus () { + if (issueIdArray == null || statusToBeUpdated == null || ignoreReason == null) { + throw new IllegalStateException(); + } + + Bson update = Updates.set(TestingRunIssues.TEST_RUN_ISSUES_STATUS, statusToBeUpdated); + + if (statusToBeUpdated == TestRunIssueStatus.IGNORED) { //Changing status to ignored + update = Updates.combine(update, Updates.set(TestingRunIssues.IGNORE_REASON, ignoreReason)); + } else { + update = Updates.combine(update, Updates.unset(TestingRunIssues.IGNORE_REASON)); + } + TestingRunIssuesDao.instance.updateMany(Filters.in(ID, issueIdArray), update); + return SUCCESS.toUpperCase(); + } + public List getIssues() { + return issues; + } + + public void setIssues(List issues) { + this.issues = issues; + } + + public TestingIssuesId getIssueId() { + return issueId; + } + + public void setIssueId(TestingIssuesId issueId) { + this.issueId = issueId; + } + + public TestRunIssueStatus getStatusToBeUpdated() { + return statusToBeUpdated; + } + + public void setStatusToBeUpdated(TestRunIssueStatus statusToBeUpdated) { + this.statusToBeUpdated = statusToBeUpdated; + } + + public String getIgnoreReason() { + return ignoreReason; + } + + public void setIgnoreReason(String ignoreReason) { + this.ignoreReason = ignoreReason; + } + + public int getSkip() { + return skip; + } + + public void setSkip(int skip) { + this.skip = skip; + } + + public int getLimit() { + return limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public long getTotalIssuesCount() { + return totalIssuesCount; + } + + public void setTotalIssuesCount(long totalIssuesCount) { + this.totalIssuesCount = totalIssuesCount; + } + + public List getFilterStatus() { + return filterStatus; + } + + public void setFilterStatus(List filterStatus) { + this.filterStatus = filterStatus; + } + + public List getFilterCollectionsId() { + return filterCollectionsId; + } + + public void setFilterCollectionsId(List filterCollectionsId) { + this.filterCollectionsId = filterCollectionsId; + } + + public List getFilterSeverity() { + return filterSeverity; + } + + public void setFilterSeverity(List filterSeverity) { + this.filterSeverity = filterSeverity; + } + + public List getFilterSubCategory() { + return filterSubCategory; + } + + public void setFilterSubCategory(List filterSubCategory) { + this.filterSubCategory = filterSubCategory; + } + + public int getStartEpoch() { + return startEpoch; + } + + public void setStartEpoch(int startEpoch) { + this.startEpoch = startEpoch; + } + + public List getIssueIdArray() { + return issueIdArray; + } + + public void setIssueIdArray(List issueIdArray) { + this.issueIdArray = issueIdArray; + } + + public TestingRunResult getTestingRunResult() { + return testingRunResult; + } + + public void setTestingRunResult(TestingRunResult testingRunResult) { + this.testingRunResult = testingRunResult; + } + + public TestSubCategory[] getSubCategories() { + return this.subCategories; + } + + public List getSimilarlyAffectedIssues() { + return similarlyAffectedIssues; + } + + public void setSimilarlyAffectedIssues(List similarlyAffectedIssues) { + this.similarlyAffectedIssues = similarlyAffectedIssues; + } + + public TestCategory[] getCategories() { + return this.categories; + } + + public void setCategories(TestCategory[] categories) { + this.categories = categories; + } + + public List getTestSourceConfigs() { + return testSourceConfigs; + } + + public void setTestSourceConfigs(List testSourceConfigs) { + this.testSourceConfigs = testSourceConfigs; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/tpi/GoogleAuthAction.java b/apps/dashboard/src/main/java/com/akto/action/tpi/GoogleAuthAction.java new file mode 100644 index 0000000000..3d42f0d62d --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/tpi/GoogleAuthAction.java @@ -0,0 +1,122 @@ +package com.akto.action.tpi; + +import com.akto.action.UserAction; +import com.akto.dao.ConfigsDao; +import com.akto.dao.ThirdPartyAccessDao; +import com.akto.dao.context.Context; +import com.akto.dto.Config; +import com.akto.dto.third_party_access.Credential; +import com.akto.dto.third_party_access.GoogleCredential; +import com.akto.dto.third_party_access.ThirdPartyAccess; +import com.akto.listener.InitializerListener; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest; +import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; +import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.services.sheets.v4.Sheets; +import com.google.api.services.sheets.v4.model.ValueRange; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.opensymphony.xwork2.Action; +import org.bson.conversions.Bson; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.mongodb.client.model.Filters.and; +import static com.mongodb.client.model.Filters.eq; + +public class GoogleAuthAction extends UserAction { + + private BasicDBObject googleConfigResult; + public String retrieveGoogleConfig() { + Config.GoogleConfig googleConfig =(Config.GoogleConfig) ConfigsDao.instance.findOne( + Filters.eq("configType", Config.ConfigType.GOOGLE.name()) + ); + if (googleConfig== null) return Action.ERROR.toUpperCase(); + + googleConfigResult = new BasicDBObject(); + googleConfigResult.put("client_id",googleConfig.getClientId()); + return Action.SUCCESS.toUpperCase(); + } + + private String code; + private String accessToken; + private int thirdPartyId; + + public String sendGoogleAuthCodeToServer() { + try { + Config.GoogleConfig googleConfig =(Config.GoogleConfig) ConfigsDao.instance.findOne("_id", "GOOGLE-ankush"); + + GoogleTokenResponse tokenResponse = + new GoogleAuthorizationCodeTokenRequest( + new NetHttpTransport(), + JacksonFactory.getDefaultInstance(), + "https://oauth2.googleapis.com/token", + googleConfig.getClientId(), + googleConfig.getClientSecret(), + code, + InitializerListener.getDomain()) + .execute(); + + this.accessToken = tokenResponse.getAccessToken(); + + String refreshToken = tokenResponse.getRefreshToken(); + + int id = Context.getId(); + + GoogleIdToken.Payload payload = tokenResponse.parseIdToken().getPayload(); + String userId = payload.get("name") + " - " + payload.getEmail(); + + ThirdPartyAccessDao.instance.insertOne( + new ThirdPartyAccess( + Context.now(), + getSUser().getId(), + 0, + new GoogleCredential(accessToken, refreshToken, tokenResponse.getExpiresInSeconds(), userId+"") + ) + ); + + this.thirdPartyId = id; + + this.code = userId; + + } catch (IOException e) { + ; + return Action.ERROR.toUpperCase(); + } + + return Action.SUCCESS.toUpperCase(); + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public int getThirdPartyId() { + return thirdPartyId; + } + + public void setThirdPartyId(int thirdPartyId) { + this.thirdPartyId = thirdPartyId; + } + + public BasicDBObject getGoogleConfigResult() { + return googleConfigResult; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/user/UserInfoAction.java b/apps/dashboard/src/main/java/com/akto/action/user/UserInfoAction.java new file mode 100644 index 0000000000..88c209f7ec --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/user/UserInfoAction.java @@ -0,0 +1,24 @@ +package com.akto.action.user; + +import com.akto.action.UserAction; +import com.akto.dao.UsersDao; +import com.opensymphony.xwork2.Action; + +public class UserInfoAction extends UserAction { + + @Override + public String execute() throws Exception { + return Action.SUCCESS; + } + + private Integer lastLoginTs; + public String fetchUserLastLoginTs() { + lastLoginTs = UsersDao.instance.fetchUserLasLoginTs(getSUser().getId()); + if (lastLoginTs == null) return ERROR.toUpperCase(); + return Action.SUCCESS.toUpperCase(); + } + + public Integer getLastLoginTs() { + return lastLoginTs; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/filter/AuthorizationFilter.java b/apps/dashboard/src/main/java/com/akto/filter/AuthorizationFilter.java new file mode 100644 index 0000000000..0e4585e329 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/filter/AuthorizationFilter.java @@ -0,0 +1,35 @@ +package com.akto.filter; + +import com.akto.dto.User; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +// If user not present in session set by UserDetailsFilter then sends error +// which will make Vue.js to redirect to login page +public class AuthorizationFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) { } + + @Override + public void destroy() {} + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest; + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + HttpSession session = httpServletRequest.getSession(); + + User user = (User) session.getAttribute("user"); + if (user == null && !httpServletRequest.getRequestURI().contains("api/googleConfig")) { + httpServletResponse.sendError(401); + } + filterChain.doFilter(servletRequest, servletResponse); + + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/filter/HttpMethodFilter.java b/apps/dashboard/src/main/java/com/akto/filter/HttpMethodFilter.java new file mode 100644 index 0000000000..4f45e08304 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/filter/HttpMethodFilter.java @@ -0,0 +1,36 @@ +package com.akto.filter; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class HttpMethodFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpServletRequest= (HttpServletRequest) request; + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + String method = httpServletRequest.getMethod(); + if (method == null) { + httpServletResponse.sendError(404); + return ; + } + + if (!method.equalsIgnoreCase("POST") && !method.equalsIgnoreCase("GET")) { + httpServletResponse.sendError(404); + return ; + } + + chain.doFilter(httpServletRequest, httpServletResponse); + } + + @Override + public void destroy() { + } +} diff --git a/apps/dashboard/src/main/java/com/akto/filter/InfraMetricsFilter.java b/apps/dashboard/src/main/java/com/akto/filter/InfraMetricsFilter.java new file mode 100644 index 0000000000..07cd0698ce --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/filter/InfraMetricsFilter.java @@ -0,0 +1,61 @@ +package com.akto.filter; + +import com.akto.listener.InfraMetricsListener; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Tag; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InfraMetricsFilter implements Filter { + + private static final Logger logger = LoggerFactory.getLogger(InfraMetricsFilter.class); + + @Override + public void init(FilterConfig filterConfig) { } + + @Override + public void destroy() { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { + filterChain.doFilter(request, response); + try { + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + + int statusCode = httpServletResponse.getStatus(); + String uri = httpServletRequest.getRequestURI(); + String method = httpServletRequest.getMethod(); + + ArrayList tags = new ArrayList<>(Arrays.asList( + Tag.of("uri", uri), + Tag.of("method", method) + )); + + if (statusCode >= 200 && statusCode< 300) { + tags.add(Tag.of("status", "good")); + } else { + tags.add(Tag.of("status", "bad")); + } + + Counter.builder("api_requests_total") + .description("API Requests Total") + .tags(tags) + .register(InfraMetricsListener.registry) + .increment(); + } catch (Exception e) { + logger.error("Inframetrics filter Error: ", e);; + } + + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/filter/MetaInfoFilter.java b/apps/dashboard/src/main/java/com/akto/filter/MetaInfoFilter.java new file mode 100644 index 0000000000..dcbc59b654 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/filter/MetaInfoFilter.java @@ -0,0 +1,35 @@ +package com.akto.filter; + +import com.akto.action.ProfileAction; +import com.akto.dto.User; +import com.mongodb.BasicDBObject; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +public class MetaInfoFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + + HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest; + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + + HttpSession session = httpServletRequest.getSession(); + +// ProfileAction.executeMeta1((User) session.getAttribute("user"), httpServletRequest); + filterChain.doFilter(servletRequest, servletResponse); + } + + @Override + public void destroy() { + + } +} diff --git a/apps/dashboard/src/main/java/com/akto/filter/RateLimitFilter.java b/apps/dashboard/src/main/java/com/akto/filter/RateLimitFilter.java new file mode 100644 index 0000000000..c88b94d572 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/filter/RateLimitFilter.java @@ -0,0 +1,90 @@ +package com.akto.filter; + +import com.akto.dao.context.Context; +import com.akto.utils.RateLimitCache; +import io.github.bucket4j.*; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.time.Duration; +import java.util.concurrent.ConcurrentHashMap; + +public class RateLimitFilter implements Filter { + private final RateLimitCache cache = new RateLimitCache(); + + + public Bucket resolveBucket(String ip, String path) { + ConcurrentHashMap val ; + if (path.equals("/auth/login")) { + val = cache.cacheMap.get(RateLimitCache.CACHE_TYPE.SIGN_IN); + } else { + val = cache.cacheMap.get(RateLimitCache.CACHE_TYPE.SEND_EMAIL); + } + + // ideally cleanup should be in separate thread, but I don't see such large number of concurrent users in near future. + if (val.size() > 10_000) { + try { + cache.deleteOldData(); + } catch (Exception e) { + ; + val.clear(); + } + } + + if (!val.containsKey(ip)) { + Bucket bucket = newBucket(path); + RateLimitCache.IpInfo ipInfo = new RateLimitCache.IpInfo(bucket, Context.now()); + val.put(ip, ipInfo); + } + + RateLimitCache.IpInfo ipInfo = val.get(ip); + + ipInfo.lastTimestamp = Context.now(); + + return ipInfo.bucket; + } + + private Bucket newBucket(String path) { + Refill refill = Refill.intervally(10, Duration.ofHours(1)); + Bandwidth limit = Bandwidth.classic(10, refill); + + return Bucket.builder().addLimit(limit).build(); + } + + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpServletRequest= (HttpServletRequest) request; + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + + String requestURI = httpServletRequest.getRequestURI(); + String ip = httpServletRequest.getRemoteAddr(); + + if (ip == null) { + httpServletResponse.sendError(401); + return ; + } + + Bucket bucket = resolveBucket(ip, requestURI); + + if (!bucket.tryConsume(1)) { + httpServletResponse.sendError(429); + return ; + } + + + chain.doFilter(httpServletRequest, httpServletResponse); + + } + + @Override + public void destroy() { + + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } +} diff --git a/apps/dashboard/src/main/java/com/akto/filter/SecurityHeadersFilter.java b/apps/dashboard/src/main/java/com/akto/filter/SecurityHeadersFilter.java new file mode 100644 index 0000000000..cf9f3b12ce --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/filter/SecurityHeadersFilter.java @@ -0,0 +1,37 @@ +package com.akto.filter; + +import javax.servlet.*; +import javax.servlet.http.HttpServletResponse; + +import com.akto.utils.HttpUtils; + +import java.io.IOException; + + +public class SecurityHeadersFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + + httpServletResponse.addHeader("X-Frame-Options", "deny"); + httpServletResponse.addHeader("X-XSS-Protection", "1"); + httpServletResponse.addHeader("X-Content-Type-Options", "nosniff"); + httpServletResponse.addHeader("cache-control", "no-cache, no-store, must-revalidate, pre-check=0, post-check=0"); + + if (HttpUtils.isHttpsEnabled()) { + httpServletResponse.addHeader("strict-transport-security","max-age=31536000; includeSubDomains; preload"); + } + + chain.doFilter(request, response); + + } + + @Override + public void destroy() { + } +} diff --git a/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java b/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java new file mode 100644 index 0000000000..8fcff47146 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/filter/UserDetailsFilter.java @@ -0,0 +1,250 @@ +package com.akto.filter; + +import com.akto.action.AccessTokenAction; +import com.akto.action.ApiTokenAction; +import com.akto.action.ProfileAction; +import com.akto.dao.ApiTokensDao; +import com.akto.dao.SignupDao; +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.ApiToken; +import com.akto.dto.SignupUserInfo; +import com.akto.dto.User; +import com.akto.dto.ApiToken.Utility; +import com.akto.utils.JWT; +import com.akto.utils.Token; +import com.mongodb.BasicDBObject; +import com.opensymphony.xwork2.Action; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.*; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static com.akto.action.LoginAction.REFRESH_TOKEN_COOKIE_NAME; + +// This is the first filter which will hit for every request to server +// First checks if the access token is valid or not (from header) +// If not then checks if it can generate valid access token using the refresh token +// Adds the access token to response header +// Using the username from the access token it sets the user details in session to be used by other filters/action +public class UserDetailsFilter implements Filter { + + public static final String LOGIN_URI = "/login"; + public static final String API_URI = "/api"; + + @Override + public void init(FilterConfig filterConfig) { } + + @Override + public void destroy() {} + + private void redirectIfNotLoginURI(FilterChain filterChain, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException { + + if (httpServletRequest.getRequestURI().contains(API_URI)) { + httpServletResponse.sendError(403); + return ; + } + + if (!httpServletRequest.getRequestURI().contains(LOGIN_URI) && !httpServletRequest.getRequestURI().contains("/auth/login") && !httpServletRequest.getRequestURI().contains("api/googleConfig")) { + httpServletResponse.sendRedirect(LOGIN_URI+"?redirect_uri="+httpServletRequest.getRequestURI()); + return; + } + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest; + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + String accessTokenFromResponse = httpServletResponse.getHeader(AccessTokenAction.ACCESS_TOKEN_HEADER_NAME); + String accessTokenFromRequest = httpServletRequest.getHeader(AccessTokenAction.ACCESS_TOKEN_HEADER_NAME); + String requestURI = httpServletRequest.getRequestURI(); + + // get api key header + String apiKey = httpServletRequest.getHeader("X-API-KEY"); + String accessToken; + + // if api key present then check if valid api key or not and generate access token + // else find access token from request header + boolean apiKeyFlag = apiKey != null; + Utility utility = null; + if (apiKeyFlag) { + if (endPointBlockedForApiToken(requestURI)) { + httpServletResponse.sendError(403); + return; + } + // check if valid key for path + ApiToken apiToken = ApiTokensDao.instance.findByKey(apiKey); + if (apiToken == null) { + httpServletResponse.sendError(403); + return; + } else { + boolean allCondition = apiToken.getUtility().getAccessList().contains(ApiToken.FULL_STRING_ALLOWED_API); + boolean pathCondition = apiToken.getUtility().getAccessList().contains(requestURI); + if (!(allCondition || pathCondition)) { + httpServletResponse.sendError(403); + return; + } + } + Context.accountId.set(apiToken.getAccountId()); + utility = apiToken.getUtility(); + + // convert apiKey to accessToken + try { + accessToken = Token.generateAccessToken(apiToken.getUsername(),"true"); + } catch (Exception e) { + ; + httpServletResponse.sendError(403); + return; + } + + } else { + if ("null".equalsIgnoreCase(accessTokenFromRequest)) { + accessTokenFromRequest = null; + } + + accessToken = accessTokenFromResponse; + if (accessToken == null) { + accessToken = accessTokenFromRequest; + } + } + + String username, signedUp; + + try { + Jws jws = JWT.parseJwt(accessToken, "/home/avneesh/Desktop/akto/dashboard/public.pem"); + username = jws.getBody().get("username").toString(); + signedUp = jws.getBody().get("signedUp").toString(); + } catch (Exception e) { + if (requestURI.contains(API_URI)) { + ((HttpServletResponse) servletResponse).sendError(403); + return ; + } + Token token = AccessTokenAction.generateAccessTokenFromServletRequest(httpServletRequest); + if (token == null) { + Cookie cookie = AccessTokenAction.generateDeleteCookie(); + httpServletResponse.addCookie(cookie); + redirectIfNotLoginURI(filterChain,httpServletRequest,httpServletResponse); + return ; + } + username = token.getUsername(); + signedUp = token.getSignedUp(); + accessToken = token.getAccessToken(); + httpServletResponse.setHeader(AccessTokenAction.ACCESS_TOKEN_HEADER_NAME, accessToken); + } + + if (username == null || signedUp == null) { + redirectIfNotLoginURI(filterChain, httpServletRequest, httpServletResponse); + return ; + } + + HttpSession session = httpServletRequest.getSession(apiKeyFlag); + // session will be non-null for external API Key requests and when session data has not been deleted + if (session == null ) { + Token tempToken = AccessTokenAction.generateAccessTokenFromServletRequest(httpServletRequest); + // If we are able to extract token from Refresh Token then this means RT is valid and new session can be created + if (tempToken== null) { + redirectIfNotLoginURI(filterChain, httpServletRequest, httpServletResponse); + return; + } + session = httpServletRequest.getSession(true); + session.setAttribute("username", username); + session.setAttribute("login", Context.now()); + session.setAttribute("signedUp", signedUp); + } + + // only for access-token based auth we check if session is valid or not + if (!apiKeyFlag) { + Object usernameObj = session.getAttribute("username"); + if (!Objects.equals(usernameObj, username)) { + redirectIfNotLoginURI(filterChain, httpServletRequest, httpServletResponse); + return ; + } + + try { + int loginTime = (int) session.getAttribute("login"); + Object logoutObj = session.getAttribute("logout"); + if (logoutObj != null) { + int logoutTime = (int) logoutObj; + if (logoutTime > loginTime) { + redirectIfNotLoginURI(filterChain, httpServletRequest, httpServletResponse); + return ; + } + } + } catch (Exception ignored) { + redirectIfNotLoginURI(filterChain, httpServletRequest, httpServletResponse); + return ; + } + + + } + + session.setAttribute(AccessTokenAction.ACCESS_TOKEN_HEADER_NAME, accessToken); + if (utility != null) session.setAttribute("utility", utility+""); // todo: replace with enum (here and haraction) + + User user = (User) session.getAttribute("user"); + boolean isSignedUp = "true".equalsIgnoreCase(signedUp); + + if ((httpServletRequest.getRequestURI().startsWith("/dashboard") || httpServletRequest.getRequestURI().startsWith("/api")) && isSignedUp) { + + // if no user details in the session, ask from DB + if (user == null || !username.equals(user.getLogin())) { + user = UsersDao.instance.findOne("login", username); + session.setAttribute("user", user); + } + + Object accountIdObj = session.getAttribute("accountId"); + String accountIdStr = accountIdObj == null ? null : accountIdObj+""; + + if (StringUtils.isEmpty(accountIdStr)) { + accountIdStr = httpServletRequest.getHeader("account"); + } + + if (StringUtils.isNotEmpty(accountIdStr)) { + int accountId = Integer.parseInt(accountIdStr); + if (accountId > 0) { + if(user.getAccounts().containsKey(accountIdStr)) { + Context.accountId.set(accountId); + } else { + } + } + } + + if (accessTokenFromRequest == null) { + ProfileAction.executeMeta1(user, httpServletRequest); + } + } else { + redirectIfNotLoginURI(filterChain, httpServletRequest, httpServletResponse); + return ; + } + filterChain.doFilter(servletRequest, servletResponse); + } + + public boolean endPointBlockedForApiToken(String endpoint) { + if (endpoint.startsWith("/dashboard")) { + return true; + } + + List blockedList = new ArrayList<>(); + blockedList.add("/api/inviteUsers"); + blockedList.add("/api/logout"); + blockedList.add("/api/getPostmanCredential"); + blockedList.add("/api/addBurpToken"); + blockedList.add("/api/addExternalApiToken"); + blockedList.add("/api/deleteApiToken"); + blockedList.add("/api/fetchApiTokens"); + + return blockedList.contains(endpoint); + } +} diff --git a/apps/dashboard/src/main/java/com/akto/listener/InfraMetricsListener.java b/apps/dashboard/src/main/java/com/akto/listener/InfraMetricsListener.java new file mode 100644 index 0000000000..1219f352ce --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/listener/InfraMetricsListener.java @@ -0,0 +1,34 @@ +package com.akto.listener; + +import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics; +import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics; +import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics; +import io.micrometer.core.instrument.binder.system.DiskSpaceMetrics; +import io.micrometer.core.instrument.binder.system.ProcessorMetrics; +import io.micrometer.core.instrument.binder.system.UptimeMetrics; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletContextListener; +import java.io.File; + +public class InfraMetricsListener implements ServletContextListener { + public static PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + private static final Logger logger = LoggerFactory.getLogger(InfraMetricsListener.class); + @Override + public void contextInitialized(javax.servlet.ServletContextEvent sce) { + + try { + new JvmThreadMetrics().bindTo(registry); + new JvmGcMetrics().bindTo(registry); + new JvmMemoryMetrics().bindTo(registry); + new DiskSpaceMetrics(new File("/")).bindTo(registry); + new ProcessorMetrics().bindTo(registry); // metrics related to the CPU stats + new UptimeMetrics().bindTo(registry); + } catch (Exception e) { + logger.error("ERROR while setting up InfraMetricsListener"); + } + } +} diff --git a/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java b/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java new file mode 100644 index 0000000000..2b80751b35 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/listener/InitializerListener.java @@ -0,0 +1,774 @@ +package com.akto.listener; + +import com.akto.DaoInit; +import com.akto.action.AdminSettingsAction; +import com.akto.action.observe.InventoryAction; +import com.akto.dao.*; +import com.akto.dao.context.Context; +import com.akto.dao.notifications.CustomWebhooksDao; +import com.akto.dao.notifications.CustomWebhooksResultDao; +import com.akto.dao.notifications.SlackWebhooksDao; +import com.akto.dao.pii.PIISourceDao; +import com.akto.dao.testing.*; +import com.akto.dao.testing.sources.TestSourceConfigsDao; +import com.akto.dto.*; +import com.akto.dto.data_types.Conditions; +import com.akto.dto.data_types.Conditions.Operator; +import com.akto.dto.data_types.Predicate; +import com.akto.dto.data_types.RegexPredicate; +import com.akto.dto.notifications.CustomWebhook; +import com.akto.dto.notifications.CustomWebhook.ActiveStatus; +import com.akto.dto.notifications.CustomWebhookResult; +import com.akto.dto.notifications.SlackWebhook; +import com.akto.dto.pii.PIISource; +import com.akto.dto.pii.PIIType; +import com.akto.dto.testing.sources.TestSourceConfig; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.notifications.slack.DailyUpdate; +import com.akto.notifications.slack.TestSummaryGenerator; +import com.akto.testing.ApiExecutor; +import com.akto.testing.ApiWorkflowExecutor; +import com.akto.util.Pair; +import com.akto.util.enums.GlobalEnums.Severity; +import com.akto.util.enums.GlobalEnums.TestCategory; +import com.akto.utils.HttpUtils; +import com.akto.utils.RedactSampleData; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.sendgrid.helpers.mail.Mail; +import com.slack.api.Slack; +import com.slack.api.webhook.WebhookResponse; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletContextListener; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import static com.akto.util.Constants.ID; +import static com.mongodb.client.model.Filters.eq; + +public class InitializerListener implements ServletContextListener { + private static final Logger logger = LoggerFactory.getLogger(InitializerListener.class); + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + + private static String domain = null; + + public static String getDomain() { + if (domain == null) { + if (true) { + domain = "https://staging.akto.io:8443"; + } else { + domain = "http://localhost:8080"; + } + } + + return domain; + } + + public void setUpPiiAndTestSourcesScheduler(){ + scheduler.scheduleAtFixedRate(new Runnable() { + public void run() { + String mongoURI = System.getenv("AKTO_MONGO_CONN"); + DaoInit.init(new ConnectionString(mongoURI)); + Context.accountId.set(1_000_000); + try { + executeTestSourcesFetch(); + } catch (Exception e) { + + } + + try { + executePIISourceFetch(); + } catch (Exception e) { + + } + } + }, 0, 4, TimeUnit.HOURS); + } + + static TestCategory findTestCategory(String path, Map shortNameToTestCategory) { + path = path.replaceAll("-", "").replaceAll("_", "").toLowerCase(); + return shortNameToTestCategory.getOrDefault(path, TestCategory.UC); + } + + static String findTestSubcategory(String path) { + String parentPath = path.substring(0, path.lastIndexOf("/")); + return parentPath.substring(parentPath.lastIndexOf("/") + 1); + } + + static void executeTestSourcesFetch() { + try { + TestCategory[] testCategories = TestCategory.values(); + Map shortNameToTestCategory = new HashMap<>(); + for (TestCategory tc : testCategories) { + String sn = tc.getShortName().replaceAll("-", "").replaceAll("_", "") + .replaceAll(" ", "").toLowerCase(); + shortNameToTestCategory.put(sn, tc); + } + + String testingSourcesRepoTree = "https://api.github.com/repos/akto-api-security/tests-library/git/trees/master?recursive=1"; + String tempFilename = "temp_testingSourcesRepoTree.json"; + FileUtils.copyURLToFile(new URL(testingSourcesRepoTree), new File(tempFilename)); + String fileContent = FileUtils.readFileToString(new File(tempFilename), StandardCharsets.UTF_8); + BasicDBObject fileList = BasicDBObject.parse(fileContent); + BasicDBList files = (BasicDBList) (fileList.get("tree")); + + BasicDBObject systemTestsQuery = new BasicDBObject(TestSourceConfig.CREATOR, TestSourceConfig.DEFAULT); + List currConfigs = TestSourceConfigsDao.instance.findAll(systemTestsQuery); + Map currConfigsMap = new HashMap<>(); + for (TestSourceConfig tsc : currConfigs) { + + if (tsc.getCategory() == null || tsc.getCategory().equals(TestCategory.UC)) { + Bson deleteQ = Filters.eq("_id", tsc.getId()); + TestSourceConfigsDao.instance.getMCollection().deleteOne(deleteQ); + } else { + currConfigsMap.put(tsc.getId(), tsc); + } + } + + if (files == null) return; + for (Object fileObj : files) { + BasicDBObject fileDetails = (BasicDBObject) fileObj; + String filePath = fileDetails.getString("path"); + if (filePath.endsWith(".yaml") || filePath.endsWith(".yml")) { + String categoryFolder = filePath.split("/")[0]; + filePath = "https://github.com/akto-api-security/tests-library/blob/master/" + filePath; + if (!currConfigsMap.containsKey(filePath)) { + TestCategory testCategory = findTestCategory(categoryFolder, shortNameToTestCategory); + String subcategory = findTestSubcategory(filePath); + TestSourceConfig testSourceConfig = new TestSourceConfig(filePath, testCategory, subcategory, Severity.HIGH, "", TestSourceConfig.DEFAULT, Context.now()); + TestSourceConfigsDao.instance.insertOne(testSourceConfig); + } + currConfigsMap.remove(filePath); + } + } + + for (String toBeDeleted : currConfigsMap.keySet()) { + TestSourceConfigsDao.instance.getMCollection().deleteOne(new BasicDBObject("_id", toBeDeleted)); + } + + + } catch (IOException e1) { + } + + + } + + static void executePIISourceFetch() { + List piiSources = PIISourceDao.instance.findAll("active", true); + for (PIISource piiSource : piiSources) { + String fileUrl = piiSource.getFileUrl(); + String id = piiSource.getId(); + Map currTypes = piiSource.getMapNameToPIIType(); + if (currTypes == null) { + currTypes = new HashMap<>(); + } + + try { + if (fileUrl.startsWith("http")) { + String tempFileUrl = "temp_" + id; + FileUtils.copyURLToFile(new URL(fileUrl), new File(tempFileUrl)); + fileUrl = tempFileUrl; + } + String fileContent = FileUtils.readFileToString(new File(fileUrl), StandardCharsets.UTF_8); + BasicDBObject fileObj = BasicDBObject.parse(fileContent); + BasicDBList dataTypes = (BasicDBList) (fileObj.get("types")); + Bson findQ = Filters.eq("_id", id); + + for (Object dtObj : dataTypes) { + BasicDBObject dt = (BasicDBObject) dtObj; + String piiKey = dt.getString("name").toUpperCase(); + PIIType piiType = new PIIType( + piiKey, + dt.getBoolean("sensitive"), + dt.getString("regexPattern"), + dt.getBoolean("onKey") + ); + + if (!dt.getBoolean("active", true)) { + PIISourceDao.instance.updateOne(findQ, Updates.unset("mapNameToPIIType." + piiKey)); + CustomDataTypeDao.instance.updateOne("name", piiKey, Updates.set("active", false)); + } + + if (currTypes.containsKey(piiKey) && currTypes.get(piiKey).equals(piiType)) { + continue; + } else { + CustomDataTypeDao.instance.deleteAll(Filters.eq("name", piiKey)); + if (!dt.getBoolean("active", true)) { + PIISourceDao.instance.updateOne(findQ, Updates.unset("mapNameToPIIType." + piiKey)); + CustomDataTypeDao.instance.insertOne(getCustomDataTypeFromPiiType(piiSource, piiType, false)); + + } else { + Bson updateQ = Updates.set("mapNameToPIIType." + piiKey, piiType); + PIISourceDao.instance.updateOne(findQ, updateQ); + CustomDataTypeDao.instance.insertOne(getCustomDataTypeFromPiiType(piiSource, piiType, true)); + } + + } + } + + } catch (IOException e) { + logger.error("failed to read file", e); + continue; + } + } + SingleTypeInfo.fetchCustomDataTypes(); + } + + private static CustomDataType getCustomDataTypeFromPiiType(PIISource piiSource, PIIType piiType, Boolean active) { + String piiKey = piiType.getName(); + + List predicates = new ArrayList<>(); + Conditions conditions = new Conditions(predicates, Operator.OR); + predicates.add(new RegexPredicate(piiType.getRegexPattern())); + IgnoreData ignoreData = new IgnoreData(new HashMap<>(), new HashSet<>()); + CustomDataType ret = new CustomDataType( + piiKey, + piiType.getIsSensitive(), + Collections.emptyList(), + piiSource.getAddedByUser(), + active, + conditions, + (piiType.getOnKey() ? null : conditions), + Operator.OR, + ignoreData + ); + + return ret; + } + + private void setUpDailyScheduler() { + scheduler.scheduleAtFixedRate(new Runnable() { + public void run() { + try { + Context.accountId.set(1_000_000); + List listWebhooks = SlackWebhooksDao.instance.findAll(new BasicDBObject()); + if (listWebhooks == null || listWebhooks.isEmpty()) { + return; + } + + Slack slack = Slack.getInstance(); + + for(SlackWebhook slackWebhook: listWebhooks) { + int now =Context.now(); + + if (slackWebhook.getFrequencyInSeconds() == 0) { + slackWebhook.setFrequencyInSeconds(24 * 60 * 60); + } + + boolean shouldSend = (slackWebhook.getLastSentTimestamp() + slackWebhook.getFrequencyInSeconds()) <= now; + + if (!shouldSend) { + continue; + } + + logger.info(slackWebhook.toString()); + + ChangesInfo ci = getChangesInfo(now - slackWebhook.getLastSentTimestamp(), now - slackWebhook.getLastSentTimestamp()); + if (ci == null || (ci.newEndpointsLast7Days.size() + ci.newSensitiveParams.size() + ci.recentSentiiveParams + ci.newParamsInExistingEndpoints) == 0) { + return; + } + + DailyUpdate dailyUpdate = new DailyUpdate( + 0, 0, + ci.newSensitiveParams.size(), ci.newEndpointsLast7Days.size(), + ci.recentSentiiveParams, ci.newParamsInExistingEndpoints, + slackWebhook.getLastSentTimestamp(), now, + ci.newSensitiveParams, slackWebhook.getDashboardUrl()); + + slackWebhook.setLastSentTimestamp(now); + SlackWebhooksDao.instance.updateOne(eq("webhook", slackWebhook.getWebhook()), Updates.set("lastSentTimestamp", now)); + + logger.info("******************DAILY INVENTORY SLACK******************"); + String webhookUrl = slackWebhook.getWebhook(); + String payload = dailyUpdate.toJSON(); + logger.info(payload); + WebhookResponse response = slack.send(webhookUrl, payload); + logger.info("*********************************************************"); + + // slack testing notification + logger.info("******************TESTING SUMMARY SLACK******************"); + TestSummaryGenerator testSummaryGenerator = new TestSummaryGenerator(1_000_000); + payload = testSummaryGenerator.toJson(slackWebhook.getDashboardUrl()); + logger.info(payload); + response = slack.send(webhookUrl, payload); + logger.info("*********************************************************"); + + } + + } catch (Exception ex) { + } + } + }, 0, 5, TimeUnit.MINUTES); + + } + + public static void webhookSenderUtil(CustomWebhook webhook) { + int now = Context.now(); + + boolean shouldSend = (webhook.getLastSentTimestamp() + webhook.getFrequencyInSeconds()) <= now; + + if (webhook.getActiveStatus() != ActiveStatus.ACTIVE || !shouldSend) { + return; + } + + ChangesInfo ci = getChangesInfo(now - webhook.getLastSentTimestamp(), now - webhook.getLastSentTimestamp()); + if (ci == null || (ci.newEndpointsLast7Days.size() + ci.newSensitiveParams.size() + ci.recentSentiiveParams + ci.newParamsInExistingEndpoints) == 0) { + return; + } + + List errors = new ArrayList<>(); + + Map valueMap = new HashMap<>(); + + valueMap.put("AKTO.changes_info.newSensitiveEndpoints", ci.newSensitiveParamsObject); + valueMap.put("AKTO.changes_info.newSensitiveEndpointsCount", ci.newSensitiveParams.size()); + + valueMap.put("AKTO.changes_info.newEndpoints", ci.newEndpointsLast7DaysObject); + valueMap.put("AKTO.changes_info.newEndpointsCount", ci.newEndpointsLast7Days.size()); + + valueMap.put("AKTO.changes_info.newSensitiveParametersCount", ci.recentSentiiveParams); + valueMap.put("AKTO.changes_info.newParametersCount", ci.newParamsInExistingEndpoints); + + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + String payload = null; + + try { + payload = apiWorkflowExecutor.replaceVariables(webhook.getBody(), valueMap, false); + } catch (Exception e) { + errors.add("Failed to replace variables"); + } + + webhook.setLastSentTimestamp(now); + CustomWebhooksDao.instance.updateOne(Filters.eq("_id", webhook.getId()), Updates.set("lastSentTimestamp", now)); + + Map> headers = OriginalHttpRequest.buildHeadersMap(webhook.getHeaderString()); + OriginalHttpRequest request = new OriginalHttpRequest(webhook.getUrl(), webhook.getQueryParams(), webhook.getMethod().toString(), payload, headers, ""); + OriginalHttpResponse response = null; // null response means api request failed. Do not use new OriginalHttpResponse() in such cases else the string parsing fails. + + try { + response = ApiExecutor.sendRequest(request,true); + logger.info("webhook request sent"); + } catch(Exception e){ + errors.add("API execution failed"); + } + + String message = null; + try { + message = RedactSampleData.convertOriginalReqRespToString(request, response); + } catch (Exception e) { + errors.add("Failed converting sample data"); + } + + CustomWebhookResult webhookResult = new CustomWebhookResult(webhook.getId(), webhook.getUserEmail(), now, message, errors); + CustomWebhooksResultDao.instance.insertOne(webhookResult); + } + + public void webhookSender() { + try { + List listWebhooks = CustomWebhooksDao.instance.findAll(new BasicDBObject()); + if (listWebhooks == null || listWebhooks.isEmpty()) { + return; + } + + for (CustomWebhook webhook : listWebhooks) { + webhookSenderUtil(webhook); + } + + } catch (Exception ex) { + } + } + + public void setUpWebhookScheduler() { + scheduler.scheduleAtFixedRate(new Runnable() { + public void run() { + String mongoURI = System.getenv("AKTO_MONGO_CONN"); + DaoInit.init(new ConnectionString(mongoURI)); + Context.accountId.set(1_000_000); + + webhookSender(); + } + }, 0, 15, TimeUnit.MINUTES); + } + + static class ChangesInfo { + public Map newSensitiveParams = new HashMap<>(); + public List newSensitiveParamsObject = new ArrayList<>(); + public List newEndpointsLast7Days = new ArrayList<>(); + public List newEndpointsLast7DaysObject = new ArrayList<>(); + public List newEndpointsLast31Days = new ArrayList<>(); + public List newEndpointsLast31DaysObject = new ArrayList<>(); + public int totalSensitiveParams = 0; + public int recentSentiiveParams = 0; + public int newParamsInExistingEndpoints = 0; + } + + public static class UrlResult { + String urlString; + BasicDBObject urlObject; + + public UrlResult(String urlString, BasicDBObject urlObject) { + this.urlString = urlString; + this.urlObject = urlObject; + } + } + + + public static UrlResult extractUrlFromBasicDbObject(BasicDBObject singleTypeInfo, Map apiCollectionMap) { + String method = singleTypeInfo.getString("method"); + String path = singleTypeInfo.getString("url"); + + Object apiCollectionIdObj = singleTypeInfo.get("apiCollectionId"); + + String urlString; + + BasicDBObject urlObject = new BasicDBObject(); + if (apiCollectionIdObj == null) { + urlString = method + " " + path; + urlObject.put("host", null); + urlObject.put("path", path); + urlObject.put("method", method); + return new UrlResult(urlString, urlObject); + } + + int apiCollectionId = (int) apiCollectionIdObj; + ApiCollection apiCollection = apiCollectionMap.get(apiCollectionId); + + String hostName = apiCollection != null ? apiCollection.getHostName() : ""; + String url; + if (hostName != null) { + url = path.startsWith("/") ? hostName + path : hostName + "/" + path; + } else { + url = path; + } + + urlString = method + " " + url; + + urlObject = new BasicDBObject(); + urlObject.put("host", hostName); + urlObject.put("path", path); + urlObject.put("method", method); + + return new UrlResult(urlString, urlObject); + } + + protected static ChangesInfo getChangesInfo(int newEndpointsFrequency, int newSensitiveParamsFrequency) { + try { + + ChangesInfo ret = new ChangesInfo(); + int now = Context.now(); + List newEndpointsSmallerDuration = new InventoryAction().fetchRecentEndpoints(now - newSensitiveParamsFrequency, now); + List newEndpointsBiggerDuration = new InventoryAction().fetchRecentEndpoints(now - newEndpointsFrequency, now); + + Map apiCollectionMap = ApiCollectionsDao.instance.generateApiCollectionMap(); + + int newParamInNewEndpoint = 0; + + for (BasicDBObject singleTypeInfo : newEndpointsSmallerDuration) { + newParamInNewEndpoint += (int) singleTypeInfo.getOrDefault("countTs", 0); + singleTypeInfo = (BasicDBObject) (singleTypeInfo.getOrDefault("_id", new BasicDBObject())); + UrlResult urlResult = extractUrlFromBasicDbObject(singleTypeInfo, apiCollectionMap); + ret.newEndpointsLast7Days.add(urlResult.urlString); + ret.newEndpointsLast7DaysObject.add(urlResult.urlObject); + } + + for (BasicDBObject singleTypeInfo : newEndpointsBiggerDuration) { + singleTypeInfo = (BasicDBObject) (singleTypeInfo.getOrDefault("_id", new BasicDBObject())); + UrlResult urlResult = extractUrlFromBasicDbObject(singleTypeInfo, apiCollectionMap); + ret.newEndpointsLast31Days.add(urlResult.urlString); + ret.newEndpointsLast31DaysObject.add(urlResult.urlObject); + } + + List sensitiveParamsList = new InventoryAction().fetchSensitiveParams(); + ret.totalSensitiveParams = sensitiveParamsList.size(); + ret.recentSentiiveParams = 0; + int delta = newSensitiveParamsFrequency; + Map, Set> endpointToSubTypes = new HashMap<>(); + for (SingleTypeInfo sti : sensitiveParamsList) { + ApiCollection apiCollection = apiCollectionMap.get(sti.getApiCollectionId()); + String url = sti.getUrl(); + if (apiCollection != null && apiCollection.getHostName() != null) { + String hostName = apiCollection.getHostName(); + url = url.startsWith("/") ? hostName + url : hostName + "/" + url; + } + + String encoded = Base64.getEncoder().encodeToString((sti.getUrl() + " " + sti.getMethod()).getBytes()); + String link = "/dashboard/observe/inventory/" + sti.getApiCollectionId() + "/" + encoded; + Pair key = new Pair<>(sti.getMethod() + " " + url, link); + String value = sti.getSubType().getName(); + if (sti.getTimestamp() >= now - delta) { + ret.recentSentiiveParams++; + Set subTypes = endpointToSubTypes.get(key); + if (subTypes == null) { + subTypes = new HashSet<>(); + endpointToSubTypes.put(key, subTypes); + } + subTypes.add(value); + } + } + + for (Pair key : endpointToSubTypes.keySet()) { + String subTypes = StringUtils.join(endpointToSubTypes.get(key), ","); + String methodPlusUrl = key.getFirst(); + ret.newSensitiveParams.put(methodPlusUrl + ": " + subTypes, key.getSecond()); + + BasicDBObject basicDBObject = new BasicDBObject(); + String[] methodPlusUrlList = methodPlusUrl.split(" "); + if (methodPlusUrlList.length != 2) continue; + basicDBObject.put("url", methodPlusUrlList[1]); + basicDBObject.put("method", methodPlusUrlList[0]); + basicDBObject.put("subTypes", subTypes); + ret.newSensitiveParamsObject.add(basicDBObject); + } + + List allNewParameters = new InventoryAction().fetchAllNewParams(now - newEndpointsFrequency, now); + int totalNewParameters = allNewParameters.size(); + ret.newParamsInExistingEndpoints = Math.max(0, totalNewParameters - newParamInNewEndpoint); + + return ret; + } catch (Exception e) { + logger.error("get new endpoints", e); + } + + return null; + } + + public void dropFilterSampleDataCollection(BackwardCompatibility backwardCompatibility) { + if (backwardCompatibility.getDropFilterSampleData() == 0) { + FilterSampleDataDao.instance.getMCollection().drop(); + } + BackwardCompatibilityDao.instance.updateOne( + Filters.eq("_id", backwardCompatibility.getId()), + Updates.set(BackwardCompatibility.DROP_FILTER_SAMPLE_DATA, Context.now()) + ); + } + + public void dropAuthMechanismData(BackwardCompatibility authMechanismData) { + if (authMechanismData.getAuthMechanismData() == 0) { + AuthMechanismsDao.instance.getMCollection().drop(); + } + BackwardCompatibilityDao.instance.updateOne( + Filters.eq("_id", authMechanismData.getId()), + Updates.set(BackwardCompatibility.AUTH_MECHANISM_DATA, Context.now()) + ); + } + + public void dropWorkflowTestResultCollection(BackwardCompatibility backwardCompatibility) { + if (backwardCompatibility.getDropWorkflowTestResult() == 0) { + WorkflowTestResultsDao.instance.getMCollection().drop(); + } + BackwardCompatibilityDao.instance.updateOne( + Filters.eq("_id", backwardCompatibility.getId()), + Updates.set(BackwardCompatibility.DROP_WORKFLOW_TEST_RESULT, Context.now()) + ); + } + + public void resetSingleTypeInfoCount(BackwardCompatibility backwardCompatibility) { + if (backwardCompatibility.getResetSingleTypeInfoCount() == 0) { + SingleTypeInfoDao.instance.resetCount(); + } + + BackwardCompatibilityDao.instance.updateOne( + Filters.eq("_id", backwardCompatibility.getId()), + Updates.set(BackwardCompatibility.RESET_SINGLE_TYPE_INFO_COUNT, Context.now()) + ); + } + + public void dropSampleDataIfEarlierNotDroped(AccountSettings accountSettings) { + if (accountSettings == null) return; + if (accountSettings.isRedactPayload() && !accountSettings.isSampleDataCollectionDropped()) { + AdminSettingsAction.dropCollections(Context.accountId.get()); + } + + } + + public void deleteAccessListFromApiToken(BackwardCompatibility backwardCompatibility) { + if (backwardCompatibility.getDeleteAccessListFromApiToken() == 0) { + ApiTokensDao.instance.updateMany(new BasicDBObject(), Updates.unset("accessList")); + } + + BackwardCompatibilityDao.instance.updateOne( + Filters.eq("_id", backwardCompatibility.getId()), + Updates.set(BackwardCompatibility.DELETE_ACCESS_LIST_FROM_API_TOKEN, Context.now()) + ); + } + + public void readyForNewTestingFramework(BackwardCompatibility backwardCompatibility) { + if (backwardCompatibility.getReadyForNewTestingFramework() == 0) { + TestingRunDao.instance.getMCollection().drop(); + TestingRunResultDao.instance.getMCollection().drop(); + TestingSchedulesDao.instance.getMCollection().drop(); + WorkflowTestResultsDao.instance.getMCollection().drop(); + + BackwardCompatibilityDao.instance.updateOne( + Filters.eq("_id", backwardCompatibility.getId()), + Updates.set(BackwardCompatibility.READY_FOR_NEW_TESTING_FRAMEWORK, Context.now()) + ); + } + } + + public void addAktoDataTypes(BackwardCompatibility backwardCompatibility) { + if (backwardCompatibility.getAddAktoDataTypes() == 0) { + List aktoDataTypes = new ArrayList<>(); + int now = Context.now(); + IgnoreData ignoreData = new IgnoreData(new HashMap<>(), new HashSet<>()); + aktoDataTypes.add(new AktoDataType("JWT", false, Arrays.asList(SingleTypeInfo.Position.RESPONSE_PAYLOAD, SingleTypeInfo.Position.RESPONSE_HEADER), now, ignoreData)); + aktoDataTypes.add(new AktoDataType("EMAIL", true, Collections.emptyList(), now, ignoreData)); + aktoDataTypes.add(new AktoDataType("CREDIT_CARD", true, Collections.emptyList(), now, ignoreData)); + aktoDataTypes.add(new AktoDataType("SSN", true, Collections.emptyList(), now, ignoreData)); + aktoDataTypes.add(new AktoDataType("ADDRESS", true, Collections.emptyList(), now, ignoreData)); + aktoDataTypes.add(new AktoDataType("IP_ADDRESS", false, Arrays.asList(SingleTypeInfo.Position.RESPONSE_PAYLOAD, SingleTypeInfo.Position.RESPONSE_HEADER), now, ignoreData)); + aktoDataTypes.add(new AktoDataType("PHONE_NUMBER", true, Collections.emptyList(), now, ignoreData)); + aktoDataTypes.add(new AktoDataType("UUID", false, Collections.emptyList(), now, ignoreData)); + AktoDataTypeDao.instance.getMCollection().drop(); + AktoDataTypeDao.instance.insertMany(aktoDataTypes); + + BackwardCompatibilityDao.instance.updateOne( + Filters.eq("_id", backwardCompatibility.getId()), + Updates.set(BackwardCompatibility.ADD_AKTO_DATA_TYPES, Context.now()) + ); + } + } + + @Override + public void contextInitialized(javax.servlet.ServletContextEvent sce) { + sce.getServletContext().getSessionCookieConfig().setSecure(HttpUtils.isHttpsEnabled()); + + logger.info("context initialized"); + + // String mongoURI = "mongodb://write_ops:write_ops@cluster0-shard-00-00.yg43a.mongodb.net:27017,cluster0-shard-00-01.yg43a.mongodb.net:27017,cluster0-shard-00-02.yg43a.mongodb.net:27017/myFirstDatabase?ssl=true&replicaSet=atlas-qd3mle-shard-0&authSource=admin&retryWrites=true&w=majority"; + String mongoURI = System.getenv("AKTO_MONGO_CONN"); + logger.info("MONGO URI " + mongoURI); + + + DaoInit.init(new ConnectionString(mongoURI)); + + Context.accountId.set(1_000_000); + SingleTypeInfoDao.instance.createIndicesIfAbsent(); + TestRolesDao.instance.createIndicesIfAbsent(); + + ApiInfoDao.instance.createIndicesIfAbsent(); + BackwardCompatibility backwardCompatibility = BackwardCompatibilityDao.instance.findOne(new BasicDBObject()); + if (backwardCompatibility == null) { + backwardCompatibility = new BackwardCompatibility(); + BackwardCompatibilityDao.instance.insertOne(backwardCompatibility); + } + + // backward compatibility + try { + dropFilterSampleDataCollection(backwardCompatibility); + resetSingleTypeInfoCount(backwardCompatibility); + dropWorkflowTestResultCollection(backwardCompatibility); + readyForNewTestingFramework(backwardCompatibility); + addAktoDataTypes(backwardCompatibility); + updateDeploymentStatus(backwardCompatibility); + dropAuthMechanismData(backwardCompatibility); + deleteAccessListFromApiToken(backwardCompatibility); + + SingleTypeInfo.init(); + + Context.accountId.set(1000000); + + if (PIISourceDao.instance.findOne("_id", "A") == null) { + String fileUrl = "https://raw.githubusercontent.com/akto-api-security/pii-types/master/general.json"; + PIISource piiSource = new PIISource(fileUrl, 0, 1638571050, 0, new HashMap<>(), true); + piiSource.setId("A"); + + PIISourceDao.instance.insertOne(piiSource); + } + + setUpDailyScheduler(); + setUpWebhookScheduler(); + setUpPiiAndTestSourcesScheduler(); + + AccountSettings accountSettings = AccountSettingsDao.instance.findOne(AccountSettingsDao.generateFilter()); + dropSampleDataIfEarlierNotDroped(accountSettings); + } catch (Exception e) { + logger.error("error while setting up dashboard: " + e.getMessage()); + } + + try { + AccountSettingsDao.instance.updateVersion(AccountSettings.DASHBOARD_VERSION); + } catch (Exception e) { + logger.error("error while updating dashboard version: " + e.getMessage()); + } + + try { + readAndSaveBurpPluginVersion(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + public static int burpPluginVersion = -1; + + public void readAndSaveBurpPluginVersion() { + URL url = this.getClass().getResource("/Akto.jar"); + if (url == null) return; + + try (JarFile jarFile = new JarFile(url.getPath())) { + Enumeration jarEntries = jarFile.entries(); + + while (jarEntries.hasMoreElements()) { + JarEntry entry = jarEntries.nextElement(); + if (entry.getName().contains("AktoVersion.txt")) { + InputStream inputStream = jarFile.getInputStream(entry); + String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + burpPluginVersion = Integer.parseInt(result.trim()); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + + } + + public void updateDeploymentStatus(BackwardCompatibility backwardCompatibility) { + String ownerEmail = System.getenv("OWNER_EMAIL"); + if (ownerEmail == null) { + logger.info("Owner email missing, might be an existing customer, skipping sending an slack and mixpanel alert"); + return; + } + if (backwardCompatibility.isDeploymentStatusUpdated()) { + logger.info("Deployment status has already been updated, skipping this"); + return; + } + String body = "{\n \"ownerEmail\": \"" + ownerEmail + "\",\n \"stackStatus\": \"COMPLETED\",\n \"cloudType\": \"AWS\"\n}"; + String headers = "{\"Content-Type\": \"application/json\"}"; + OriginalHttpRequest request = new OriginalHttpRequest(getUpdateDeploymentStatusUrl(), "", "POST", body, OriginalHttpRequest.buildHeadersMap(headers), ""); + try { + OriginalHttpResponse response = ApiExecutor.sendRequest(request, false); + logger.info("Update deployment status reponse: {}", response.getBody()); + } catch (Exception e) { + logger.error("Failed to update deployment status, will try again on next boot up", e); + return; + } + BackwardCompatibilityDao.instance.updateOne( + Filters.eq("_id", backwardCompatibility.getId()), + Updates.set(BackwardCompatibility.DEPLOYMENT_STATUS_UPDATED, true) + ); + } + + private String getUpdateDeploymentStatusUrl() { + String url = System.getenv("UPDATE_DEPLOYMENT_STATUS_URL"); + return url != null ? url : "https://stairway.akto.io/deployment/status"; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/listener/KafkaListener.java b/apps/dashboard/src/main/java/com/akto/listener/KafkaListener.java new file mode 100644 index 0000000000..98d2116bb0 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/listener/KafkaListener.java @@ -0,0 +1,28 @@ +package com.akto.listener; + + +import com.akto.kafka.Kafka; +import com.akto.utils.DashboardMode; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletContextListener; + +public class KafkaListener implements ServletContextListener { + public static Kafka kafka; + public static final int BATCH_SIZE_CONFIG = 999900; + private static final Logger logger = LoggerFactory.getLogger(KafkaListener.class); + @Override + public void contextInitialized(javax.servlet.ServletContextEvent sce) { + String brokerIP = "kafka1:19092"; //System.getenv("AKTO_KAFKA_BROKER_URL"); + if (brokerIP != null && !DashboardMode.isLocalDeployment()) { + try { + kafka = new Kafka(brokerIP, 1000, BATCH_SIZE_CONFIG); + } catch (Exception e) { + logger.error("ERROR while setting up KafkaListener"); + } + } + + } +} diff --git a/apps/dashboard/src/main/java/com/akto/listener/RuntimeListener.java b/apps/dashboard/src/main/java/com/akto/listener/RuntimeListener.java new file mode 100644 index 0000000000..7854c4cdb1 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/listener/RuntimeListener.java @@ -0,0 +1,24 @@ +package com.akto.listener; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import com.akto.dao.context.Context; +import com.akto.parsers.HttpCallParser; +import com.akto.runtime.Main; +import com.akto.runtime.policies.AktoPolicy; + +public class RuntimeListener implements ServletContextListener { + + public static HttpCallParser httpCallParser = null; + public static AktoPolicy aktoPolicy = null; + + @Override + public void contextInitialized(ServletContextEvent sce) { + Context.accountId.set(1_000_000); + Main.initializeRuntime(); + httpCallParser = new HttpCallParser("userIdentifier", 1, 1, 1, false); + aktoPolicy = new AktoPolicy(RuntimeListener.httpCallParser.apiCatalogSync, false); + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/AktoCustomException.java b/apps/dashboard/src/main/java/com/akto/utils/AktoCustomException.java new file mode 100644 index 0000000000..236e4ec69a --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/AktoCustomException.java @@ -0,0 +1,7 @@ +package com.akto.utils; + +public class AktoCustomException extends Exception{ + public AktoCustomException(String message) { + super(message); + } +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/DashboardMode.java b/apps/dashboard/src/main/java/com/akto/utils/DashboardMode.java new file mode 100644 index 0000000000..826bc9c234 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/DashboardMode.java @@ -0,0 +1,29 @@ +package com.akto.utils; + +import org.apache.commons.lang3.StringUtils; + +public enum DashboardMode { + LOCAL_DEPLOY, ON_PREM; + + public static DashboardMode getDashboardMode(){ + String dashboardMode = System.getenv("DASHBOARD_MODE"); + if(StringUtils.isEmpty(dashboardMode)){ + return ON_PREM; + } + if("local_deploy".equalsIgnoreCase(dashboardMode)){ + return LOCAL_DEPLOY; + } + return ON_PREM; + } + + public static boolean isLocalDeployment(){ + DashboardMode dashboardMode = DashboardMode.getDashboardMode(); + return dashboardMode.equals(LOCAL_DEPLOY); + } + + public static boolean isOnPremDeployment(){ + DashboardMode dashboardMode = DashboardMode.getDashboardMode(); + return dashboardMode.equals(ON_PREM); + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/HtmlCleanPolicy.java b/apps/dashboard/src/main/java/com/akto/utils/HtmlCleanPolicy.java new file mode 100644 index 0000000000..2c92c4f88f --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/HtmlCleanPolicy.java @@ -0,0 +1,155 @@ +package com.akto.utils; + +import org.owasp.html.HtmlPolicyBuilder; +import org.owasp.html.PolicyFactory; + +import java.util.function.Predicate; +import java.util.regex.Pattern; + +public class HtmlCleanPolicy { + + private static Predicate matchesEither( + final Pattern a, final Pattern b) { + return s -> a.matcher(s).matches() || b.matcher(s).matches(); + } + + // Some common regular expression definitions. + + // The 16 colors defined by the HTML Spec (also used by the CSS Spec) + private static final Pattern COLOR_NAME = Pattern.compile( + "(?:aqua|black|blue|fuchsia|gray|grey|green|lime|maroon|navy|olive|purple" + + "|red|silver|teal|white|yellow)"); + + // HTML/CSS Spec allows 3 or 6 digit hex to specify color + private static final Pattern COLOR_CODE = Pattern.compile( + "(?:#(?:[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?))"); + + private static final Pattern NUMBER_OR_PERCENT = Pattern.compile( + "[0-9]+%?"); + private static final Pattern PARAGRAPH = Pattern.compile( + "(?:[\\p{L}\\p{N},'\\.\\s\\-_\\(\\)]|&[0-9]{2};)*"); + private static final Pattern HTML_ID = Pattern.compile( + "[a-zA-Z0-9\\:\\-_\\.]+"); + // force non-empty with a '+' at the end instead of '*' + private static final Pattern HTML_TITLE = Pattern.compile( + "[\\p{L}\\p{N}\\s\\-_',:\\[\\]!\\./\\\\\\(\\)&]*"); + private static final Pattern HTML_CLASS = Pattern.compile( + "[a-zA-Z0-9\\s,\\-_]+"); + + private static final Pattern ONSITE_URL = Pattern.compile( + "(?:[\\p{L}\\p{N}\\\\\\.\\#@\\$%\\+&;\\-_~,\\?=/!]+|\\#(\\w)+)"); + private static final Pattern OFFSITE_URL = Pattern.compile( + "\\s*(?:(?:ht|f)tps?://|mailto:)[\\p{L}\\p{N}]" + + "[\\p{L}\\p{N}\\p{Zs}\\.\\#@\\$%\\+&;:\\-_~,\\?=/!\\(\\)]*+\\s*"); + + private static final Pattern NUMBER = Pattern.compile( + "[+-]?(?:(?:[0-9]+(?:\\.[0-9]*)?)|\\.[0-9]+)"); + + private static final Pattern POSITIVE_INTEGERS= Pattern.compile( + "\\b(? COLOR_NAME_OR_COLOR_CODE + = matchesEither(COLOR_NAME, COLOR_CODE); + + private static final Predicate ONSITE_OR_OFFSITE_URL + = matchesEither(ONSITE_URL, OFFSITE_URL); + + private static final Pattern HISTORY_BACK = Pattern.compile( + "(?:javascript:)?\\Qhistory.go(-1)\\E"); + + private static final Pattern ONE_CHAR = Pattern.compile( + ".?", Pattern.DOTALL); + + + public static final PolicyFactory POLICY_DEFINITION = new HtmlPolicyBuilder() + .allowAttributes("id").matching(HTML_ID).globally() + .allowAttributes("class").matching(HTML_CLASS).globally() + .allowAttributes("lang").matching(Pattern.compile("[a-zA-Z]{2,20}")) + .globally() + .allowAttributes("title").matching(HTML_TITLE).globally() + .allowStyling() + .allowAttributes("align").matching(ALIGN).onElements("p") + .allowAttributes("for").matching(HTML_ID).onElements("label") + .allowAttributes("color").matching(COLOR_NAME_OR_COLOR_CODE::test) + .onElements("font") + .allowAttributes("face") + .matching(Pattern.compile("[\\w;, \\-]+")) + .onElements("font") + .allowAttributes("size").matching(NUMBER).onElements("font") + .allowAttributes("href").matching(ONSITE_OR_OFFSITE_URL::test) + .onElements("a") + .allowStandardUrlProtocols() + .allowAttributes("nohref").onElements("a") + .allowAttributes("name").matching(NAME).onElements("a") + .allowAttributes( + "onfocus", "onblur", "onclick", "onmousedown", "onmouseup") + .matching(HISTORY_BACK).onElements("a") + .requireRelNofollowOnLinks() + .allowAttributes("src").matching(ONSITE_OR_OFFSITE_URL::test) + .onElements("img") + .allowAttributes("name").matching(NAME) + .onElements("img") + .allowAttributes("alt").matching(PARAGRAPH) + .onElements("img") + .allowAttributes("border", "hspace", "vspace").matching(NUMBER) + .onElements("img") + .allowAttributes("border", "cellpadding", "cellspacing") + .matching(NUMBER).onElements("table") + .allowAttributes("bgcolor").matching(COLOR_NAME_OR_COLOR_CODE::test) + .onElements("table") + .allowAttributes("background").matching(ONSITE_URL) + .onElements("table") + .allowAttributes("align").matching(ALIGN) + .onElements("table") + .allowAttributes("noresize").matching(Pattern.compile("(?i)noresize")) + .onElements("table") + .allowAttributes("background").matching(ONSITE_URL) + .onElements("td", "th", "tr") + .allowAttributes("bgcolor").matching(COLOR_NAME_OR_COLOR_CODE::test) + .onElements("td", "th") + .allowAttributes("abbr").matching(PARAGRAPH) + .onElements("td", "th") + .allowAttributes("axis", "headers").matching(NAME) + .onElements("td", "th") + .allowAttributes("scope") + .matching(Pattern.compile("(?i)(?:row|col)(?:group)?")) + .onElements("td", "th") + .allowAttributes("nowrap") + .onElements("td", "th") + .allowAttributes("height", "width").matching(NUMBER_OR_PERCENT) + .onElements("table", "td", "th", "tr", "img") + .allowAttributes("align").matching(ALIGN) + .onElements("thead", "tbody", "tfoot", "img", + "td", "th", "tr", "colgroup", "col") + .allowAttributes("valign").matching(VALIGN) + .onElements("thead", "tbody", "tfoot", + "td", "th", "tr", "colgroup", "col") + .allowAttributes("charoff").matching(NUMBER_OR_PERCENT) + .onElements("td", "th", "tr", "colgroup", "col", + "thead", "tbody", "tfoot") + .allowAttributes("char").matching(ONE_CHAR) + .onElements("td", "th", "tr", "colgroup", "col", + "thead", "tbody", "tfoot") + .allowAttributes("colspan", "rowspan").matching(NUMBER) + .onElements("td", "th") + .allowAttributes("span", "width").matching(NUMBER_OR_PERCENT) + .onElements("colgroup", "col") + .allowAttributes("data-mention-id").matching(POSITIVE_INTEGERS).globally() + .allowElements( + "a", "label", "noscript", "h1", "h2", "h3", "h4", "h5", "h6", + "p", "i", "b", "u", "strong", "em", "small", "big", "pre", "code", + "cite", "samp", "sub", "sup", "strike", "center", "blockquote", + "hr", "br", "col", "font", "map", "span", "div", "img", + "ul", "ol", "li", "dd", "dt", "dl", "tbody", "thead", "tfoot", + "table", "td", "th", "tr", "colgroup", "fieldset", "legend") + .toFactory(); +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/HttpUtils.java b/apps/dashboard/src/main/java/com/akto/utils/HttpUtils.java new file mode 100644 index 0000000000..a708373ffd --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/HttpUtils.java @@ -0,0 +1,13 @@ +package com.akto.utils; + +import java.util.Objects; + +public class HttpUtils { + + private static final String https = System.getenv("AKTO_HTTPS_FLAG"); + + public static boolean isHttpsEnabled(){ + return Objects.equals(https, "true"); + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/JWT.java b/apps/dashboard/src/main/java/com/akto/utils/JWT.java new file mode 100644 index 0000000000..708800ea4b --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/JWT.java @@ -0,0 +1,99 @@ +package com.akto.utils; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; + +import java.io.*; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.Calendar; +import java.util.Map; + +public class JWT { + + private static final byte[] privateKey, publicKey; + + static { + KeyPairGenerator kpg; + KeyPair kp = null; + try { + kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048); + kp = kpg.generateKeyPair(); + + } catch (NoSuchAlgorithmException e) { + ; + } + + privateKey = kp == null ? null : kp.getPrivate().getEncoded(); + publicKey = kp == null ? null : kp.getPublic().getEncoded(); + } + + public static String createJWT(String privateKeyPath, Map claims, + String issuer, String subject, int expiryUnit, int expiryDuration) + throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { + + PrivateKey privateKey = getPrivateKey(privateKeyPath); + + Calendar calendar = Calendar.getInstance(); + java.util.Date issueTime = calendar.getTime(); + + calendar.setTime(issueTime); + calendar.add(expiryUnit, expiryDuration); + java.util.Date expiryTime = calendar.getTime(); + + + return Jwts.builder() + .setIssuer(issuer) + .setSubject(subject) + .addClaims(claims) + .setIssuedAt(issueTime) + .setExpiration(expiryTime) + .signWith(privateKey) + .compact(); + } + + public static Jws parseJwt(String jwsString, String publicKeyPath) + throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { + + PublicKey publicKey= getPublicKey(publicKeyPath); + + return Jwts.parserBuilder() + .setSigningKey(publicKey) + .build() + .parseClaimsJws(jwsString); + } + + + private static PrivateKey getPrivateKey(String privateKeyPath) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePrivate(keySpec); + } + + private static PublicKey getPublicKey(String publicKeyPath) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePublic(keySpec); + } + + private static String readKey(String keyPath) throws IOException { + File f = new File(keyPath); + FileInputStream fis = new FileInputStream(f); + DataInputStream dis = new DataInputStream(fis); + byte[] keyBytes = new byte[(int) f.length()]; + dis.readFully(keyBytes); + dis.close(); + + return new String(keyBytes); + } +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/Mention.java b/apps/dashboard/src/main/java/com/akto/utils/Mention.java new file mode 100644 index 0000000000..0d699b8030 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/Mention.java @@ -0,0 +1,32 @@ +package com.akto.utils; + +import com.akto.dao.UsersDao; +import com.mongodb.BasicDBObject; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Mention { + + public static ArrayList extractMentionedUsersID(String content) { + ArrayList mentioned_users = new ArrayList<>(); + + Pattern pattern = Pattern.compile("data-mention-id=\"(\\d*)\""); + Matcher matcher = pattern.matcher(content); + + while (matcher.find()) { + try { + int userId = Integer.parseInt(matcher.group(1)); + BasicDBObject mentionedUser = UsersDao.instance.getUserInfo(userId); + if ( mentionedUser != null) { + mentioned_users.add(userId); + } + } catch (NumberFormatException e) { + e.getMessage(); + } + } + + return mentioned_users; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/RandomString.java b/apps/dashboard/src/main/java/com/akto/utils/RandomString.java new file mode 100644 index 0000000000..cb127cbbf4 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/RandomString.java @@ -0,0 +1,36 @@ +package com.akto.utils; + +import java.security.SecureRandom; +import java.util.Locale; +import java.util.Random; + +public class RandomString { + public String nextString() { + for (int idx = 0; idx < buf.length; ++idx) + buf[idx] = symbols[random.nextInt(symbols.length)]; + return new String(buf); + } + + public static final String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + public static final String lower = upper.toLowerCase(Locale.ROOT); + + public static final String digits = "0123456789"; + + public static final String alphanum = upper + lower + digits; + + private final Random random; + + private final char[] symbols; + + private final char[] buf; + + public RandomString(int length) { + if (length < 1) throw new IllegalArgumentException(); + this.random = new SecureRandom(); + this.symbols = alphanum.toCharArray(); + this.buf = new char[length]; + } + + +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/RateLimitCache.java b/apps/dashboard/src/main/java/com/akto/utils/RateLimitCache.java new file mode 100644 index 0000000000..4df05c5549 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/RateLimitCache.java @@ -0,0 +1,56 @@ +package com.akto.utils; + +import com.akto.dao.context.Context; +import io.github.bucket4j.Bucket; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class RateLimitCache { + + public static int thresholdForDeletion = 60*60*5; + + public Map> cacheMap; + + public RateLimitCache() { + cacheMap = new HashMap<>(); + cacheMap.put(CACHE_TYPE.SIGN_IN, new ConcurrentHashMap<>()); + cacheMap.put(CACHE_TYPE.SEND_EMAIL, new ConcurrentHashMap<>()); + } + + public enum CACHE_TYPE { + SIGN_IN, SEND_EMAIL + } + public static class IpInfo { + public Bucket bucket; + public int lastTimestamp; + + public IpInfo(Bucket bucket, int lastTimestamp) { + this.bucket = bucket; + this.lastTimestamp = lastTimestamp; + } + + } + + + public void deleteOldData() { + for (Map ipInfoMap : this.cacheMap.values() ) { + Iterator> iterator = ipInfoMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + int lastTimestamp = entry.getValue().lastTimestamp; + int diff = Context.now() - lastTimestamp; + if (diff > thresholdForDeletion) { + iterator.remove(); + } + } + } + } + + + + + +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/Token.java b/apps/dashboard/src/main/java/com/akto/utils/Token.java new file mode 100644 index 0000000000..81b7f8fc1c --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/Token.java @@ -0,0 +1,62 @@ +package com.akto.utils; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +// To initialise Token we need a valid refresh token. +// Valid refresh token conditions: 1. Valid JWT 2. Should exist in db correctly mapped to user +public class Token { + private String refreshToken = null; + private String accessToken = null; + private String username = null; + + private String signedUp = "false"; + + public static String generateAccessToken(String username, String signedUp) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { + Map claims = new HashMap<>(); + claims.put("username", username); + claims.put("signedUp", signedUp); + + return JWT.createJWT( + "/home/avneesh/Desktop/akto/dashboard/private.pem", + claims, + "Akto", + "login", + Calendar.MINUTE, + 15 + ); + + } + + public Token(String refreshToken) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { + Jws jws = JWT.parseJwt(refreshToken, "/home/avneesh/Desktop/akto/dashboard/public.pem"); + this.username = jws.getBody().get("username").toString(); + this.signedUp = jws.getBody().get("signedUp").toString(); + this.refreshToken = refreshToken; + + this.accessToken = generateAccessToken(this.username, this.signedUp); + } + + public String getAccessToken() { + return accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public String getUsername() { + return username; + } + + public String getSignedUp() { + return signedUp; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/Utils.java b/apps/dashboard/src/main/java/com/akto/utils/Utils.java new file mode 100644 index 0000000000..d85d550a28 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/Utils.java @@ -0,0 +1,239 @@ +package com.akto.utils; + +import com.akto.dao.ThirdPartyAccessDao; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.third_party_access.Credential; +import com.akto.dto.third_party_access.PostmanCredential; +import com.akto.dto.third_party_access.ThirdPartyAccess; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.listener.KafkaListener; +import com.akto.listener.RuntimeListener; +import com.akto.parsers.HttpCallParser; +import com.akto.runtime.APICatalogSync; +import com.akto.runtime.policies.AktoPolicy; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; + +import java.sql.Timestamp; +import java.util.*; + +import com.mongodb.client.model.Filters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class Utils { + + private static final Logger logger = LoggerFactory.getLogger(Utils.class); + private final static ObjectMapper mapper = new ObjectMapper(); + public static Map getVariableMap(ArrayNode variables){ + Map result = new HashMap<>(); + if(variables == null){ + return result; + } + for(JsonNode variable : variables){ + result.put(variable.get("key").asText(), variable.get("value").asText()); + } + return result; + } + + public static Map convertApiInAktoFormat(JsonNode apiInfo, Map variables, String accountId) { + try { + JsonNode request = apiInfo.get("request"); + JsonNode response = apiInfo.has("response") ? apiInfo.get("response").get(0): null; + String apiName = apiInfo.get("name").asText(); + if(response == null){ + logger.info("There are no responses for this api {}, skipping this", apiName); + return null; + } + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + + Map result = new HashMap<>(); + result.put("akto_account_id", accountId); + result.put("path", getPath(request, variables)); + Map requestHeadersMap = getHeaders((ArrayNode) request.get("header")); + Map responseHeadersMap = getHeaders((ArrayNode) response.get("header")); + try { + result.put("requestHeaders", mapper.writeValueAsString(requestHeadersMap)); + result.put("responseHeaders", mapper.writeValueAsString(responseHeadersMap)); + } catch (JsonProcessingException e) { + logger.error("Error while processing request/response headers", e); + } + + String contentType = getContentType(request, response, requestHeadersMap); + String requestPayload; + if(contentType.contains("json")){ + String body = response.get("body").asText(); + if(body == null){ + body = "{}"; + } + if(body.startsWith("[")){ + requestPayload = body; + } else { + Map bodyMap = mapper.readValue(body, new TypeReference>() {}); + requestPayload = mapper.writeValueAsString(bodyMap); + } + } else if(contentType.contains("x-www-form-urlencoded")){ + String postText = response.get("body").asText(); + if (postText == null) { + postText = ""; + } + requestPayload = postText; + } else { + logger.info("Unsupported content type {} for api {}",contentType, apiName); + return null; + } + + result.put("method", request.get("method").asText()); + result.put("requestPayload", requestPayload); + result.put("responsePayload", response.get("body").asText()); + result.put("ip", "null"); + result.put("time", Long.toString(timestamp.getTime() / 1000L)); + result.put("statusCode", response.get("code").asText()); + result.put("type", "http"); + result.put("status", response.get("status").asText()); + result.put("contentType", contentType); + result.put("source", "POSTMAN"); + + return result; + } catch (Exception e){ + logger.error("Failed to convert postman obj to Akto format", e); + return null; + } + } + + private static String getContentType(JsonNode request, JsonNode response, Map responseHeadersMap) { + if(responseHeadersMap.containsKey("content-type")){ + return responseHeadersMap.get("content-type"); + } + if(request.has("body")){ + JsonNode body = request.get("body"); + if(body.has("options")){ + JsonNode options = request.get("options"); + if(options.has("raw")){ + JsonNode raw = request.get("raw"); + if(raw.has("language")){ + return raw.get("language").asText(); + } + } + } + } + return response.get("_postman_previewlanguage").asText(); + } + + public static String getPath(JsonNode request, Map variables){ + JsonNode urlObj = request.get("url"); + String url = urlObj.get("raw").asText(); + if((url.contains("{{") && url.contains("}}")) && !variables.isEmpty()){ + String host = process((ArrayNode) urlObj.get("host"), ".", variables); + String path = process((ArrayNode) urlObj.get("path"), "/", variables); + url = ""; + if(urlObj.has("protocol") && urlObj.get("protocol").asText().length() > 0){ + url += urlObj.get("protocol").asText(); + url += "://"; + } + if(host.endsWith("/")){ + url += host.substring(0, host.length()-1); + } + else{ + url += host; + } + if(urlObj.has("port") && urlObj.get("port").asText().length() > 0){ + url += ":" + urlObj.get("port").asText(); + } + if(!(path.startsWith("/") || path.startsWith("."))){ + url += "/"; + } + url += path; + } + return url; + } + + public static String process(ArrayNode arrayNode, String delimiter, Map variables){ + ArrayList variableReplacedHostList = new ArrayList<>(); + for (JsonNode jsonNode : arrayNode) { + String hostPart = jsonNode.asText(); + if (hostPart.contains("{{") && hostPart.contains("}}")) { + hostPart = extractVariableAndReplace(hostPart, variables); + } + variableReplacedHostList.add(hostPart); + } + return String.join(delimiter, variableReplacedHostList); + } + + private static String extractVariableAndReplace(String str, Map variables) { + int start = str.indexOf('{'); + int end = str.lastIndexOf('}'); + String key = str.substring(start+2, end-1); + if(variables.containsKey(key)){ + String val = variables.get(key); + str = str.replace("{{" + key + "}}", val); + } + return str; + } + + + private static Map getHeaders(ArrayNode headers){ + Map result = new HashMap<>(); + for(JsonNode node: headers){ + result.put(node.get("key").asText().toLowerCase(), node.get("value").asText()); + } + return result; + } + + public static void fetchApisRecursively(ArrayNode items, ArrayList jsonNodes) { + if(items == null || items.size() == 0){ + return; + } + for(JsonNode item: items){ + if(item.has("item")){ + fetchApisRecursively( (ArrayNode) item.get("item"), jsonNodes); + } else { + jsonNodes.add(item); + } + } + + } + + public static void pushDataToKafka(int apiCollectionId, String topic, List messages, List errors, boolean skipKafka) throws Exception { + List responses = new ArrayList<>(); + for (String message: messages){ + if (message.length() < 0.8 * KafkaListener.BATCH_SIZE_CONFIG) { + if (!skipKafka) { + KafkaListener.kafka.send(message,"har_" + topic); + } else { + HttpResponseParams responseParams = HttpCallParser.parseKafkaMessage(message); + responseParams.getRequestParams().setApiCollectionId(apiCollectionId); + responses.add(responseParams); + } + } else { + errors.add("Message too big size: " + message.length()); + } + } + + if(skipKafka) { + SingleTypeInfo.fetchCustomDataTypes(); //todo: + APICatalogSync apiCatalogSync = RuntimeListener.httpCallParser.syncFunction(responses, true, false); + RuntimeListener.aktoPolicy.main(responses, apiCatalogSync, false); + } + } + + public static PostmanCredential fetchPostmanCredential(int userId) { + ThirdPartyAccess thirdPartyAccess = ThirdPartyAccessDao.instance.findOne( + Filters.and( + Filters.eq("owner", userId), + Filters.eq("credential.type", Credential.Type.POSTMAN) + ) + ); + + if (thirdPartyAccess == null) { + return null; + } + + return (PostmanCredential) thirdPartyAccess.getCredential(); + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/cloud/CloudType.java b/apps/dashboard/src/main/java/com/akto/utils/cloud/CloudType.java new file mode 100644 index 0000000000..b7b5042833 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/cloud/CloudType.java @@ -0,0 +1,5 @@ +package com.akto.utils.cloud; + +public enum CloudType { + AWS, GCP, Azure; +} \ No newline at end of file diff --git a/apps/dashboard/src/main/java/com/akto/utils/cloud/Utils.java b/apps/dashboard/src/main/java/com/akto/utils/cloud/Utils.java new file mode 100644 index 0000000000..45e8ef4d35 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/cloud/Utils.java @@ -0,0 +1,33 @@ +package com.akto.utils.cloud; + +import com.amazonaws.services.cloudformation.AmazonCloudFormation; +import com.amazonaws.services.cloudformation.AmazonCloudFormationClientBuilder; +import com.amazonaws.services.cloudformation.model.DescribeStacksRequest; +import com.amazonaws.services.cloudformation.model.DescribeStacksResult; +import com.amazonaws.services.cloudformation.model.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +public class Utils { + + private static final Logger logger = LoggerFactory.getLogger(Utils.class); + public static CloudType getCloudType() { + if (System.getenv("AWS_REGION") != null) { + return CloudType.AWS; + } + return CloudType.GCP; + } + + public static List fetchTags(String stackName){ + DescribeStacksRequest describeStackRequest = new DescribeStacksRequest(); + describeStackRequest.setStackName(stackName); + AmazonCloudFormation cloudFormation = AmazonCloudFormationClientBuilder.standard() + .build(); + DescribeStacksResult result = cloudFormation.describeStacks(describeStackRequest); + com.amazonaws.services.cloudformation.model.Stack stack = result.getStacks().get(0); + return stack.getTags(); + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/cloud/serverless/ServerlessFunction.java b/apps/dashboard/src/main/java/com/akto/utils/cloud/serverless/ServerlessFunction.java new file mode 100644 index 0000000000..250bac0351 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/cloud/serverless/ServerlessFunction.java @@ -0,0 +1,10 @@ +package com.akto.utils.cloud.serverless; + +public interface ServerlessFunction { + + public void invokeFunction(String functionName) throws Exception; + + public void updateFunctionConfiguration(String functionName, UpdateFunctionRequest updateFunctionRequest) + throws Exception; + +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/cloud/serverless/UpdateFunctionRequest.java b/apps/dashboard/src/main/java/com/akto/utils/cloud/serverless/UpdateFunctionRequest.java new file mode 100644 index 0000000000..b275736963 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/cloud/serverless/UpdateFunctionRequest.java @@ -0,0 +1,15 @@ +package com.akto.utils.cloud.serverless; + +import java.util.Map; + +public class UpdateFunctionRequest { + private final Map environmentVariables; + + public UpdateFunctionRequest(Map envVarsMap) { + this.environmentVariables = envVarsMap; + } + + public Map getEnvironmentVariables() { + return this.environmentVariables; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/cloud/serverless/aws/Lambda.java b/apps/dashboard/src/main/java/com/akto/utils/cloud/serverless/aws/Lambda.java new file mode 100644 index 0000000000..553eb605cd --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/cloud/serverless/aws/Lambda.java @@ -0,0 +1,114 @@ +package com.akto.utils.cloud.serverless.aws; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.akto.utils.cloud.serverless.ServerlessFunction; +import com.akto.utils.cloud.serverless.UpdateFunctionRequest; +import com.amazonaws.services.lambda.AWSLambda; +import com.amazonaws.services.lambda.AWSLambdaClientBuilder; +import com.amazonaws.services.lambda.model.AWSLambdaException; +import com.amazonaws.services.lambda.model.Environment; +import com.amazonaws.services.lambda.model.EnvironmentResponse; +import com.amazonaws.services.lambda.model.FunctionConfiguration; +import com.amazonaws.services.lambda.model.GetFunctionRequest; +import com.amazonaws.services.lambda.model.GetFunctionResult; +import com.amazonaws.services.lambda.model.InvokeRequest; +import com.amazonaws.services.lambda.model.InvokeResult; +import com.amazonaws.services.lambda.model.UpdateFunctionConfigurationRequest; + +public class Lambda implements ServerlessFunction { + + private static Lambda instance = null; + + public static final Lambda getInstance(){ + if(instance == null){ + instance = new Lambda(); + } + return instance; + } + + private Lambda(){ + + } + + private static final Logger logger = LoggerFactory.getLogger(ServerlessFunction.class); + + private static final AWSLambda awsLambda = AWSLambdaClientBuilder.standard().build(); + + @Override + public void updateFunctionConfiguration(String functionName, UpdateFunctionRequest updateFunctionRequest) + throws Exception { + + if (functionName == null || functionName.length() == 0 || updateFunctionRequest == null + || updateFunctionRequest.getEnvironmentVariables() == null) { + throw new Exception("Invalid updateFunctionConfiguration Request"); + } + + try { + GetFunctionResult function = getFunction(functionName); + FunctionConfiguration existingFunctionConfiguration = function.getConfiguration(); + EnvironmentResponse existingEnvironmentVariables = existingFunctionConfiguration.getEnvironment(); + Map existingEnvironmentVariablesMap = existingEnvironmentVariables.getVariables(); + + // Update the existingEnvVariablesMap with the new env vars and then set this + // updated map in UpdateFunctionConfigurationRequest + int keysUpdatedCount = 0; + for (Map.Entry entry : updateFunctionRequest.getEnvironmentVariables().entrySet()) { + if (existingEnvironmentVariablesMap.containsKey(entry.getKey())) { + existingEnvironmentVariablesMap.put(entry.getKey(), entry.getValue()); + keysUpdatedCount++; + } + } + + if (keysUpdatedCount == 0) { + // no env vars to update, returning + logger.info("No env vars to update for funciton: " + functionName + ", returning"); + return; + } + + UpdateFunctionConfigurationRequest req = new UpdateFunctionConfigurationRequest(); + req.setFunctionName(functionName); + Environment updatedEnvironment = new Environment(); + updatedEnvironment.setVariables(existingEnvironmentVariablesMap); + req.setEnvironment(updatedEnvironment); + + awsLambda.updateFunctionConfiguration(req); + logger.info("Succeefully updated function configuration for function: " + functionName); + } catch (Exception e) { + ; + } + } + + private GetFunctionResult getFunction(String functionName) throws Exception { + GetFunctionRequest getFunctionRequest = new GetFunctionRequest(); + getFunctionRequest.setFunctionName(functionName); + GetFunctionResult getFunctionResult = awsLambda.getFunction(getFunctionRequest); + return getFunctionResult; + } + + @Override + public void invokeFunction(String functionName) throws Exception { + + InvokeRequest invokeRequest = new InvokeRequest() + .withFunctionName(functionName) + .withPayload("{}"); + InvokeResult invokeResult = null; + try { + + logger.info("Invoke lambda "+functionName); + invokeResult = awsLambda.invoke(invokeRequest); + + String resp = new String(invokeResult.getPayload().array(), StandardCharsets.UTF_8); + logger.info("Function: {}, response: {}", functionName, resp); + } catch (AWSLambdaException e) { + logger.error(String.format("Error while invoking Lambda: %s", functionName), e); + } + + + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/cloud/stack/Stack.java b/apps/dashboard/src/main/java/com/akto/utils/cloud/stack/Stack.java new file mode 100644 index 0000000000..3353edd5c4 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/cloud/stack/Stack.java @@ -0,0 +1,22 @@ +package com.akto.utils.cloud.stack; + +import com.akto.utils.cloud.stack.dto.StackState; +import com.amazonaws.services.cloudformation.model.*; + +import java.util.List; +import java.util.Map; + +public interface Stack { + public String createStack(String stackName, Map parameters, String template, List tags) throws Exception; + + public StackState fetchStackStatus(String stackName); + + public boolean checkIfStackExists(String stackName); + + public enum StackStatus { + DOES_NOT_EXISTS, CREATE_IN_PROGRESS, FAILED_TO_INITIALIZE, CREATION_FAILED, CREATE_COMPLETE, + FAILED_TO_FETCH_STACK_STATUS; + } + + public String fetchResourcePhysicalIdByLogicalId(String stackName, String logicalId); +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/cloud/stack/aws/AwsStack.java b/apps/dashboard/src/main/java/com/akto/utils/cloud/stack/aws/AwsStack.java new file mode 100644 index 0000000000..b31ea3d4cb --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/cloud/stack/aws/AwsStack.java @@ -0,0 +1,130 @@ +package com.akto.utils.cloud.stack.aws; + +import java.util.Set; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Future; + +import com.akto.utils.cloud.stack.dto.StackState; +import com.amazonaws.services.cloudformation.AmazonCloudFormation; +import com.amazonaws.services.cloudformation.AmazonCloudFormationAsync; +import com.amazonaws.services.cloudformation.AmazonCloudFormationAsyncClientBuilder; +import com.amazonaws.services.cloudformation.AmazonCloudFormationClientBuilder; +import com.amazonaws.services.cloudformation.model.*; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AwsStack implements com.akto.utils.cloud.stack.Stack { + + private static final Logger logger = LoggerFactory.getLogger(AwsStack.class); + private static final Set ACCEPTABLE_STACK_STATUSES = new HashSet( + Arrays.asList(StackStatus.CREATE_IN_PROGRESS.toString(), StackStatus.CREATE_COMPLETE.toString())); + private static final int STACK_CREATION_TIMEOUT_MINS = 20; + private static final List STACK_CREATION_CAPABILITIES = Arrays.asList("CAPABILITY_IAM"); + private static final AmazonCloudFormationAsync CLOUD_FORMATION_ASYNC = AmazonCloudFormationAsyncClientBuilder + .standard().build(); + private static final AmazonCloudFormation CLOUD_FORMATION_SYNC = AmazonCloudFormationClientBuilder.standard() + .build(); + + private AwsStack(){ + } + + public static AwsStack instance = null; + + public static AwsStack getInstance() { + if(instance == null){ + instance = new AwsStack(); + } + return instance; + } + + @Override + public String createStack(String stackName, Map parameters, String template, List tags) throws Exception { + try { + CreateStackRequest createRequest = new CreateStackRequest(); + createRequest.setStackName(stackName); + createRequest.setTimeoutInMinutes(STACK_CREATION_TIMEOUT_MINS); + createRequest.setParameters(fetchParamters(parameters)); + createRequest.setCapabilities(STACK_CREATION_CAPABILITIES); + createRequest.setTemplateBody(template); + if(tags != null && !tags.isEmpty()) { + createRequest.setTags(tags); + } + Future future = CLOUD_FORMATION_ASYNC.createStackAsync(createRequest); + CreateStackResult createStackResult = future.get(); + logger.info("Stack Id: " + createStackResult.getStackId()); + return createStackResult.getStackId(); + } catch (Exception e) { + ; + throw e; + } + } + + private Collection fetchParamters(Map parametersMap) { + List parameters = new ArrayList<>(); + for (Map.Entry entry : parametersMap.entrySet()) { + Parameter parameter = new Parameter(); + parameter.setParameterKey(entry.getKey()); + parameter.setParameterValue(entry.getValue()); + parameters.add(parameter); + } + return parameters; + } + + @Override + public StackState fetchStackStatus(String stackName) { + DescribeStacksRequest describeStackRequest = new DescribeStacksRequest(); + describeStackRequest.setStackName(stackName); + try { + DescribeStacksResult result = CLOUD_FORMATION_SYNC.describeStacks(describeStackRequest); + Stack stack = result.getStacks().get(0); + + String stackStatus = stack.getStackStatus(); + + if (!ACCEPTABLE_STACK_STATUSES.contains(stackStatus)) { + logger.info("Actual stack status: " + stackStatus); + return new StackState(StackStatus.CREATION_FAILED.toString(), 0); + } + return new StackState(stackStatus, stack.getCreationTime().getTime()); + } catch (Exception e) { + if (e.getMessage().contains("does not exist")) { + return new StackState(StackStatus.DOES_NOT_EXISTS.toString(), 0); + } + ; + return new StackState(StackStatus.FAILED_TO_FETCH_STACK_STATUS.toString(), 0); + } + } + + public String fetchResourcePhysicalIdByLogicalId(String stackName, String logicalId){ + if(StringUtils.isEmpty(stackName) || StringUtils.isEmpty(logicalId)){ + return ""; + } + DescribeStackResourcesRequest req = new DescribeStackResourcesRequest(); + req.setStackName(stackName); + req.setLogicalResourceId(logicalId); + try { + DescribeStackResourcesResult res = CLOUD_FORMATION_SYNC.describeStackResources(req); + List resources = res.getStackResources(); + + return resources.get(0).getPhysicalResourceId(); + } catch (Exception e) { + logger.error("Failed to fetch physical id of resource with logical id {}", logicalId, e); + return ""; + } + } + + @Override + public boolean checkIfStackExists(String stackName) { + String stackStatus = fetchStackStatus(stackName).getStatus(); + return stackStatus.equals("CREATE_COMPLETE"); + } +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/cloud/stack/dto/StackState.java b/apps/dashboard/src/main/java/com/akto/utils/cloud/stack/dto/StackState.java new file mode 100644 index 0000000000..92da61f189 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/cloud/stack/dto/StackState.java @@ -0,0 +1,27 @@ +package com.akto.utils.cloud.stack.dto; + +public class StackState { + private String status; + private long creationTime; + + public StackState(String status, long creationTime) { + this.status = status; + this.creationTime = creationTime; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public long getCreationTime() { + return creationTime; + } + + public void setCreationTime(long creationTime) { + this.creationTime = creationTime; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/platform/DashboardStackDetails.java b/apps/dashboard/src/main/java/com/akto/utils/platform/DashboardStackDetails.java new file mode 100644 index 0000000000..07bbb846cf --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/platform/DashboardStackDetails.java @@ -0,0 +1,23 @@ +package com.akto.utils.platform; + +import org.apache.commons.lang3.StringUtils; + +public class DashboardStackDetails { + + + public static final String AKTO_DASHBOARD_STACK_NAME = "AKTO_DASHBOARD_STACK_NAME"; + public static String getStackName() { + return System.getenv(AKTO_DASHBOARD_STACK_NAME); + } + + public static final String AKTO_DASHBOARD_ROLE = "AktoDashboardRole"; + + + public static final String AKTO_LB_DASHBOARD = "AktoLBDashboard"; + + public static String getAktoDashboardRole(){ + String roleName = System.getenv("AKTO_DASHBOARD_ROLE_NAME"); + return StringUtils.isEmpty(roleName) ? AKTO_DASHBOARD_ROLE: roleName; + } + +} diff --git a/apps/dashboard/src/main/java/com/akto/utils/platform/MirroringStackDetails.java b/apps/dashboard/src/main/java/com/akto/utils/platform/MirroringStackDetails.java new file mode 100644 index 0000000000..c0e222f728 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/utils/platform/MirroringStackDetails.java @@ -0,0 +1,66 @@ +package com.akto.utils.platform; + +public class MirroringStackDetails { + + public static String getStackName(){ + String dashboardStackName = DashboardStackDetails.getStackName(); + if(dashboardStackName != null){ + return dashboardStackName + "-mirroring"; + } + return "akto-mirroring"; // keep this backward compatible + } + + public static final String CREATE_MIRROR_SESSION_LAMBDA = "CreateMirrorSession"; + + public static final String AKTO_CONTEXT_ANALYZER_AUTO_SCALING_GROUP = "AktoContextAnalyzerAutoScalingGroup"; + + public static final String AKTO_AUTO_SCALING_GROUP = "AktoAutoScalingGroup"; + + public static final String TRAFFIC_MIRROR_TARGET = "TrafficMirrorTarget"; + + public static final String LB_TRAFFIC_MIRROR_FILTER = "LBTrafficMirrorFilter"; + + public static final String LAMBDA_LOG_GROUP = "LambdaLogGroup"; + + public static final String LAMBDA_BASIC_EXECUTION_ROLE = "LambdaBasicExecutionRole"; + + public static final String GET_AKTO_SETUP_DETAILS_LAMBDA_BASIC_EXECUTION_ROLE = "GetAktoSetupDetailsLambdaBasicExecutionRole"; + + + public static final String AKTO_NLB = "AktoNLB"; + + public static final String AKTO_CONTEXT_ANALYZER_UPDATE_LAMBDA = "AktoContextAnalyzerInstanceRefreshHandler"; + public static final String AKTO_DASHBOARD_UPDATE_LAMBDA = "DashboardInstanceRefreshHandler"; + public static final String AKTO_RUNTIME_UPDATE_LAMBDA = "TrafficMirroringInstanceRefreshHandler"; + + public static final String GET_AKTO_SETUP_DETAILS_LAMBDA = "GetAktoSetupDetails"; + + public static final String LAMBDA_VPC_ACCESS_ROLE = "LambdaVPCAccessRole"; + + public static final String LAMBDA_SECURITY_GROUP_VPC = "LambdaSecurityGroupVPC"; + + public static final String SAVE_COLLECTION_NAMES_LAMBDA = "SaveCollectionNames"; + + public static final String GET_VPC_DETAILS_LAMBDA_ROLE = "GetVpcDetailsLambdaRole"; + + public static final String GET_VPC_DETAILS_LAMBDA = "GetVpcDetailsLambda"; + + public static final String AKTO_CONTEXT_ANALYZER_SECURITY_GROUP = "AktoContextAnalyzerSecurityGroup"; + + public static final String AKTO_CONTEXT_ANALYZER_INSTANCE_REFRESH_HANDLER_LAMBDA = "AktoContextAnalyzerInstanceRefreshHandler"; + + public static final String REFRESH_HANDLER_LAMBDA_BASIC_EXECUTION_ROLE = "RefreshHandlerLambdaBasicExecutionRole"; + + public static final String AKTO_SECURITY_GROUP = "AktoSecurityGroup"; + + public static final String AKTO_TRAFFIC_MIRRORING_TARGET_GROUP = "AktoTrafficMirroringTargetGroup"; + + public static final String AKTO_KAFKA_TARGET_GROUP = "AktoKafkaTargetGroup"; + + public static final String DASHBOARD_INSTANCE_REFRESH_HANDLER_LAMBDA = "DashboardInstanceRefreshHandler"; + + public static final String TRAFFIC_MIRRORING_INSTANCE_REFRESH_HANDLER_LAMBDA = "TrafficMirroringInstanceRefreshHandler"; + + public static final String INSTANCE_REFRESH_HANDLER_LAMBDA_ROLE = "InstanceRefreshHandlerLambdaRole"; + +} diff --git a/apps/dashboard/src/main/resources/Akto.jar b/apps/dashboard/src/main/resources/Akto.jar new file mode 100644 index 0000000000..dbaae244b2 Binary files /dev/null and b/apps/dashboard/src/main/resources/Akto.jar differ diff --git a/apps/dashboard/src/main/resources/cloud_formation_templates/akto_aws_mirroring.template b/apps/dashboard/src/main/resources/cloud_formation_templates/akto_aws_mirroring.template new file mode 100644 index 0000000000..66dd5bc4e7 --- /dev/null +++ b/apps/dashboard/src/main/resources/cloud_formation_templates/akto_aws_mirroring.template @@ -0,0 +1 @@ +{"AWSTemplateFormatVersion":"2010-09-09","Description":"This template does a simple setup for all Akto modules. It sets up all modules on a single instance. If you want a scalable and flexible setup, please contact support@akto.io.","Parameters":{"SubnetId":{"Type":"AWS::EC2::Subnet::Id"},"KeyPair":{"Type":"AWS::EC2::KeyPair::KeyName"},"SourceLBs":{"Type":"CommaDelimitedList"},"MongoIp":{"Type":"String"}},"Mappings":{"RegionMap":{"af-south-1":{"AMI":"ami-0adee70ff4394e3d5"},"eu-north-1":{"AMI":"ami-04e8b0e36ed3403dc"},"ap-south-1":{"AMI":"ami-09de362f44ba0a166"},"eu-west-3":{"AMI":"ami-0614433a16ab15878"},"eu-west-2":{"AMI":"ami-030770b178fa9d374"},"eu-south-1":{"AMI":"ami-0432f14b68c3e0273"},"eu-west-1":{"AMI":"ami-0bba0a4cb75835f71"},"ap-northeast-3":{"AMI":"ami-0253beba286f3e848"},"ap-northeast-2":{"AMI":"ami-0e1d09d8b7c751816"},"me-south-1":{"AMI":"ami-07a68e42e669daed0"},"ap-northeast-1":{"AMI":"ami-06ce6680729711877"},"sa-east-1":{"AMI":"ami-0656df2cc0dfd150a"},"ca-central-1":{"AMI":"ami-04c12937e87474def"},"ap-east-1":{"AMI":"ami-0b751f901b93720a5"},"ap-southeast-1":{"AMI":"ami-0adf622550366ea53"},"ap-southeast-2":{"AMI":"ami-03b836d87d294e89e"},"eu-central-1":{"AMI":"ami-094c442a8e9a67935"},"ap-southeast-3":{"AMI":"ami-0483d92a8124da6c9"},"us-east-1":{"AMI":"ami-065efef2c739d613b"},"us-east-2":{"AMI":"ami-07251f912d2a831a3"},"us-west-1":{"AMI":"ami-09b2f6d85764ec71b"},"us-west-2":{"AMI":"ami-0d08ef957f0e4722b"}}},"Resources":{"TrafficMirrorTarget":{"Type":"AWS::EC2::TrafficMirrorTarget","Properties":{"Description":"Traffic Mirror target set to network interface of Akto EC2 instance","Tags":[{"Key":"Name","Value":{"Fn::Join":["",[{"Ref":"AWS::StackName"},"-","Target"]]}},{"Key":"Deployment","Value":"Akto-CloudFormation"}],"NetworkLoadBalancerArn":{"Ref":"AktoNLB"}}},"LBTrafficMirrorFilter":{"Type":"AWS::EC2::TrafficMirrorFilter","Properties":{"Description":"Traffic mirror filter for LBs and Target Groups","NetworkServices":["amazon-dns"],"Tags":[{"Key":"Name","Value":{"Fn::Join":["",[{"Ref":"AWS::StackName"},"-","Filter"]]}},{"Key":"Deployment","Value":"Akto-CloudFormation"}]}},"LambdaLogGroup":{"Type":"AWS::Logs::LogGroup","Properties":{"LogGroupName":{"Fn::Sub":"/aws/lambda/${CreateMirrorSession}"},"RetentionInDays":7}},"LambdaBasicExecutionRole":{"Type":"AWS::IAM::Role","Properties":{"AssumeRolePolicyDocument":{"Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]},"Path":"/","Policies":[{"PolicyName":"LBCreateTrafficMirrorSession","PolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["ec2:DescribeNetworkInterfaces","ec2:DescribeTrafficMirrorSessions","ec2:DescribeInstances","ec2:DescribeVpcs","elasticloadbalancing:DescribeLoadBalancers","elasticloadbalancing:DescribeTargetGroups","elasticloadbalancing:DescribeTargetHealth","ec2:DescribeTrafficMirrorFilters","ec2:DeleteTrafficMirrorFilterRule","ec2:CreateTrafficMirrorFilterRule","lambda:InvokeFunction"],"Resource":"*"},{"Effect":"Allow","Action":["ec2:CreateTrafficMirrorSession"],"Resource":["arn:aws:ec2:*:*:traffic-mirror-session/*","arn:aws:ec2:*:*:network-interface/*",{"Fn::Join":["",["arn:aws:ec2:*:*:traffic-mirror-target/",{"Ref":"TrafficMirrorTarget"}]]},{"Fn::Join":["",["arn:aws:ec2:*:*:traffic-mirror-filter/*"]]}]},{"Effect":"Allow","Action":["ec2:DeleteTrafficMirrorSession"],"Resource":["arn:aws:ec2:*:*:traffic-mirror-session/*"]}]}}]}},"GetAktoSetupDetailsLambdaBasicExecutionRole":{"Type":"AWS::IAM::Role","Properties":{"AssumeRolePolicyDocument":{"Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]},"Path":"/","Policies":[{"PolicyName":"GetAktoSetupDetailsExecuteLambda","PolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["ec2:DescribeNetworkInterfaces","ec2:DescribeTrafficMirrorSessions","ec2:DescribeInstances","ec2:DescribeVpcs","elasticloadbalancing:DescribeLoadBalancers","elasticloadbalancing:DescribeTargetGroups","elasticloadbalancing:DescribeTargetHealth"],"Resource":"*"}]}}]}},"LambdaLogPermissions":{"Type":"AWS::IAM::Policy","Properties":{"Roles":[{"Ref":"LambdaBasicExecutionRole"}],"PolicyName":{"Fn::Sub":"${AWS::Region}-LambdaLogGroup"},"PolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["logs:CreateLogStream","logs:PutLogEvents"],"Resource":[{"Fn::Sub":"arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${CreateMirrorSession}"},{"Fn::Sub":"arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${CreateMirrorSession}:*"},{"Fn::Sub":"arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${CreateMirrorSession}:*:*"}]}]}}},"GetAktoSetupDetails":{"Type":"AWS::Lambda::Function","Properties":{"Runtime":"nodejs12.x","Timeout":60,"Role":{"Fn::GetAtt":["GetAktoSetupDetailsLambdaBasicExecutionRole","Arn"]},"Handler":"index.handler","Environment":{"Variables":{"TARGET_LB":{"Ref":"AktoNLB"}}},"Code":{"S3Bucket":{"Fn::Sub":"akto-setup-${AWS::Region}"},"S3Key":"templates/get-akto-setup-details.zip"}}},"CustomSourceAktoSetupDetails":{"Type":"AWS::CloudFormation::CustomResource","Properties":{"ServiceToken":{"Fn::GetAtt":["GetAktoSetupDetails","Arn"]}}},"CreateMirrorSession":{"Type":"AWS::Lambda::Function","Properties":{"Runtime":"nodejs12.x","Timeout":60,"Role":{"Fn::GetAtt":["LambdaBasicExecutionRole","Arn"]},"Handler":"index.handler","Environment":{"Variables":{"ELB_NAMES":{"Fn::Join":[",",{"Ref":"SourceLBs"}]},"TRAFFIC_MIRROR_FILTER_ID":{"Ref":"LBTrafficMirrorFilter"},"TRAFFIC_MIRROR_TARGET_ID":{"Ref":"TrafficMirrorTarget"},"TARGET_LB":{"Ref":"AktoNLB"},"SAVE_COLLECTION_NAMES_LAMBDA_ARN":{"Fn::GetAtt":["SaveCollectionNames","Arn"]}}},"Code":{"S3Bucket":{"Fn::Sub":"akto-setup-${AWS::Region}"},"S3Key":"templates/create-mirror-session.zip"},"Description":"Auto create mirroring configuration","TracingConfig":{"Mode":"Active"}}},"PeriodicEventRule":{"Type":"AWS::Events::Rule","Properties":{"Description":"Generate an event periodically","Name":{"Fn::Join":["",[{"Ref":"AWS::StackName"},"-","PeriodicRule"]]},"ScheduleExpression":"rate(15 minutes)","State":"ENABLED","Targets":[{"Arn":{"Fn::GetAtt":["CreateMirrorSession","Arn"]},"Id":{"Ref":"CreateMirrorSession"}}]}},"PeriodicEventPermission":{"Type":"AWS::Lambda::Permission","Properties":{"FunctionName":{"Fn::GetAtt":["CreateMirrorSession","Arn"]},"Action":"lambda:InvokeFunction","Principal":"events.amazonaws.com","SourceAccount":{"Ref":"AWS::AccountId"},"SourceArn":{"Fn::GetAtt":["PeriodicEventRule","Arn"]}}},"LambdaVPCAccessRole":{"Type":"AWS::IAM::Role","Properties":{"AssumeRolePolicyDocument":{"Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]},"Path":"/","Policies":[{"PolicyName":"LambdaBasicAccessVPCPolicy","PolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["ec2:DescribeNetworkInterfaces","ec2:DescribeInstances","ec2:CreateNetworkInterface","ec2:DeleteNetworkInterface","ec2:AttachNetworkInterface"],"Resource":"*"}]}}]}},"LambdaSecurityGroupVPC":{"Type":"AWS::EC2::SecurityGroup","Properties":{"VpcId":{"Fn::GetAtt":["CustomSourceAktoSetupDetails","VpcId"]},"GroupDescription":"Security group is required to create a lambda inside a VPC","SecurityGroupEgress":[{"IpProtocol":"tcp","FromPort":9092,"ToPort":9092,"CidrIp":"0.0.0.0/0"}]}},"SaveCollectionNames":{"Type":"AWS::Lambda::Function","Properties":{"Runtime":"nodejs12.x","Timeout":60,"Role":{"Fn::GetAtt":["LambdaVPCAccessRole","Arn"]},"Handler":"nodejs/index.handler","VpcConfig":{"SecurityGroupIds":[{"Fn::GetAtt":["LambdaSecurityGroupVPC","GroupId"]}],"SubnetIds":{"Fn::GetAtt":["CustomSourceAktoSetupDetails","SubnetId"]}},"Environment":{"Variables":{"PRIVATE_IP":{"Fn::GetAtt":["CustomSourceAktoSetupDetails","kafkaIp"]},"SUCCESS_ENIS":{"Fn::GetAtt":["CustomSourceAktoSetupDetails","successEnis"]}}},"Code":{"S3Bucket":{"Fn::Sub":"akto-setup-${AWS::Region}"},"S3Key":"templates/mirroring-collections-split.zip"},"Description":"Send collection name to id mapping to Akto modules","TracingConfig":{"Mode":"Active"}}},"GetVpcDetailsLambdaRole":{"Type":"AWS::IAM::Role","Properties":{"AssumeRolePolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":["lambda.amazonaws.com"]},"Action":["sts:AssumeRole"]}]},"Path":"/","Policies":[{"PolicyName":"DescribeAssetsPolicy","PolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["ec2:DescribeVpcs","ec2:DescribeSubnets"],"Resource":"*"}]}}]}},"GetVpcDetailsLambda":{"Type":"AWS::Lambda::Function","Properties":{"Description":"Look up info from a VPC","Handler":"index.handler","Runtime":"nodejs12.x","Timeout":30,"Role":{"Fn::GetAtt":["GetVpcDetailsLambdaRole","Arn"]},"Environment":{"Variables":{"SUBNET_ID":{"Ref":"SubnetId"}}},"Code":{"ZipFile":"var SUBNET_ID = process.env.SUBNET_ID; var aws = require('aws-sdk'); var response = require('cfn-response'); var ec2 = new aws.EC2(); exports.handler = async function(event, context) {\n if (event.RequestType == 'Delete') {\n await response.send(event, context, 'SUCCESS');\n return;\n }\n var params = {\n SubnetIds: [SUBNET_ID] \n };\n var subnets = await ec2.describeSubnets(params).promise().catch(err => {\n console.error(err);\n });\n var vpcId = subnets['Subnets'][0]['VpcId'];\n var vpcs = await ec2.describeVpcs({VpcIds: [vpcId]}).promise().catch(err => {\n console.error(err);\n });\n await response.send(event, context, 'SUCCESS', {CidrBlock: vpcs['Vpcs'][0]['CidrBlock'], VpcId: vpcId})\n};\n"}}},"CustomSourceGetVpcDetails":{"Type":"AWS::CloudFormation::CustomResource","Properties":{"ServiceToken":{"Fn::GetAtt":["GetVpcDetailsLambda","Arn"]}}},"IamInstanceProfile":{"Type":"AWS::IAM::InstanceProfile","Properties":{"Path":"/","Roles":[{"Ref":"RefreshHandlerLambdaBasicExecutionRole"}]}},"AktoContextAnalyzerSecurityGroup":{"Type":"AWS::EC2::SecurityGroup","Properties":{"VpcId":{"Fn::GetAtt":["CustomSourceGetVpcDetails","VpcId"]},"GroupDescription":"Enable the ports Akto requires (22, 9092)","SecurityGroupIngress":[{"IpProtocol":"tcp","FromPort":22,"ToPort":22,"CidrIp":{"Fn::GetAtt":["CustomSourceGetVpcDetails","CidrBlock"]}},{"IpProtocol":"tcp","FromPort":9092,"ToPort":9092,"CidrIp":{"Fn::GetAtt":["CustomSourceGetVpcDetails","CidrBlock"]}}],"SecurityGroupEgress":[]}},"AktoContextAnalyzerASGLaunchConfiguration":{"Type":"AWS::AutoScaling::LaunchConfiguration","Properties":{"ImageId":{"Fn::FindInMap":["RegionMap",{"Ref":"AWS::Region"},"AMI"]},"InstanceType":"m5a.xlarge","KeyName":{"Ref":"KeyPair"},"AssociatePublicIpAddress":"false","SecurityGroups":[{"Ref":"AktoContextAnalyzerSecurityGroup"}],"BlockDeviceMappings":[{"DeviceName":"/dev/xvda","Ebs":{"VolumeType":"gp2","DeleteOnTermination":"true","VolumeSize":"50","Encrypted":true}}],"MetadataOptions":{"HttpTokens":"required"},"UserData":{"Fn::Base64":{"Fn::Join":["\n",["#!/bin/bash -xe",{"Fn::Sub":"export AKTO_MONGO_CONN='${MongoIp}'"},"touch /tmp/hello.txt","touch ~/hello.txt","sudo yum update -y","sudo yum install -y python python-setuptools","sudo yum install -y docker","sudo dockerd&","sudo mkdir -p /opt/aws/bin","export COMPOSE_FILE=docker-compose-context-analyser.yml","sudo wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz","sudo python -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-latest.tar.gz","curl -fsSL 'https://raw.githubusercontent.com/akto-api-security/infra/feature/segregation_2/cf-deploy-akto' > cf-deploy-akto","sudo chmod 700 cf-deploy-akto","./cf-deploy-akto < <(echo 'test')","sudo echo >> ~/akto/infra/docker-context-analyser.env","sudo echo AKTO_MONGO_CONN=$AKTO_MONGO_CONN >> ~/akto/infra/docker-context-analyser.env","export TOKEN=$(curl -X PUT 'http://169.254.169.254/latest/api/token' -H 'X-aws-ec2-metadata-token-ttl-seconds: 600')",{"Fn::Join":[":",["export AKTO_CURRENT_INSTANCE_IP=$(curl -H \"X-aws-ec2-metadata-token","$TOKEN\" -v http://169.254.169.254/latest/meta-data/local-ipv4)"]]},"echo AKTO_CURRENT_INSTANCE_IP=$AKTO_CURRENT_INSTANCE_IP >> ~/akto/infra/docker-context-analyser.env","curl -fsSL 'https://raw.githubusercontent.com/akto-api-security/infra/feature/segregation_2/cf-deploy-akto-start' > cf-deploy-akto-start","sudo chmod 700 cf-deploy-akto-start","./cf-deploy-akto-start < <(echo 'test')"]]}}}},"AktoContextAnalyzerAutoScalingGroup":{"Type":"AWS::AutoScaling::AutoScalingGroup","Properties":{"LaunchConfigurationName":{"Ref":"AktoContextAnalyzerASGLaunchConfiguration"},"VPCZoneIdentifier":[{"Ref":"SubnetId"}],"MaxSize":"1","MinSize":"1"}},"AktoContextAnalyzerInstanceRefreshHandler":{"Type":"AWS::Lambda::Function","Properties":{"Handler":"index.handler","Runtime":"nodejs12.x","Timeout":30,"Role":{"Fn::GetAtt":["InstanceRefreshHandlerLambdaRole","Arn"]},"Code":{"ZipFile":"var aws = require('aws-sdk'); var autoscaling = new aws.AutoScaling(); exports.handler = function(event, context) {\n var params = {\n AutoScalingGroupName: 'AktoContextAnalyzerAutoScalingGroup', \n Preferences: {\n InstanceWarmup: 200, \n MinHealthyPercentage: 0\n }\n };\n \n autoscaling.startInstanceRefresh(params, function(err, data) {\n if(err) { console.log(err) }\n else { console.log(data) }\n })\n}; \n"}}},"RefreshHandlerLambdaBasicExecutionRole":{"Type":"AWS::IAM::Role","Properties":{"AssumeRolePolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ec2.amazonaws.com"},"Action":"sts:AssumeRole"}]},"Policies":[{"PolicyName":"InvokeLambdaPolicy","PolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Resource":[{"Fn::GetAtt":["DashboardInstanceRefreshHandler","Arn"]},{"Fn::GetAtt":["TrafficMirroringInstanceRefreshHandler","Arn"]},{"Fn::GetAtt":["AktoContextAnalyzerInstanceRefreshHandler","Arn"]}],"Action":"lambda:InvokeFunction"}]}}]}},"AktoSecurityGroup":{"Type":"AWS::EC2::SecurityGroup","Properties":{"VpcId":{"Fn::GetAtt":["CustomSourceGetVpcDetails","VpcId"]},"GroupDescription":"Enable the ports Akto requires (22, 4789, 8000, 9092)","SecurityGroupIngress":[{"IpProtocol":"tcp","FromPort":22,"ToPort":22,"CidrIp":{"Fn::GetAtt":["CustomSourceGetVpcDetails","CidrBlock"]}},{"IpProtocol":"tcp","FromPort":9092,"ToPort":9092,"CidrIp":{"Fn::GetAtt":["CustomSourceGetVpcDetails","CidrBlock"]}},{"IpProtocol":"udp","FromPort":4789,"ToPort":4789,"CidrIp":{"Fn::GetAtt":["CustomSourceGetVpcDetails","CidrBlock"]}},{"IpProtocol":"tcp","FromPort":8000,"ToPort":8000,"CidrIp":{"Fn::GetAtt":["CustomSourceGetVpcDetails","CidrBlock"]}}],"SecurityGroupEgress":[]}},"AktoASGLaunchConfiguration":{"Type":"AWS::AutoScaling::LaunchConfiguration","DependsOn":["AktoNLB"],"Properties":{"ImageId":{"Fn::FindInMap":["RegionMap",{"Ref":"AWS::Region"},"AMI"]},"InstanceType":"m5a.xlarge","KeyName":{"Ref":"KeyPair"},"AssociatePublicIpAddress":"false","IamInstanceProfile":{"Ref":"IamInstanceProfile"},"SecurityGroups":[{"Ref":"AktoSecurityGroup"}],"BlockDeviceMappings":[{"DeviceName":"/dev/xvda","Ebs":{"VolumeType":"gp2","DeleteOnTermination":"true","VolumeSize":"50","Encrypted":true}}],"MetadataOptions":{"HttpTokens":"required"},"UserData":{"Fn::Base64":{"Fn::Join":["\n",["#!/bin/bash -xe",{"Fn::Sub":"export AKTO_MONGO_CONN='${MongoIp}'"},{"Fn::Sub":"export AKTO_KAFKA_IP='${AktoNLB.DNSName}'"},"touch /tmp/hello.txt","touch ~/hello.txt","sudo yum update -y","sudo yum install -y python python-setuptools","sudo yum install -y docker","sudo dockerd&","sudo mkdir -p /opt/aws/bin","export COMPOSE_FILE=docker-compose-runtime.yml","sudo wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz","sudo python -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-latest.tar.gz","curl -fsSL 'https://raw.githubusercontent.com/akto-api-security/infra/feature/segregation_2/cf-deploy-akto' > cf-deploy-akto","sudo chmod 700 cf-deploy-akto","./cf-deploy-akto < <(echo 'test')","sudo echo >> ~/akto/infra/docker-runtime.env","sudo echo AKTO_MONGO_CONN=$AKTO_MONGO_CONN >> ~/akto/infra/docker-runtime.env","sudo echo AKTO_KAFKA_IP=$AKTO_KAFKA_IP >> ~/akto/infra/.env","curl -fsSL 'https://raw.githubusercontent.com/akto-api-security/infra/feature/segregation_2/cf-deploy-akto-start' > cf-deploy-akto-start","sudo chmod 700 cf-deploy-akto-start","./cf-deploy-akto-start < <(echo 'test')"]]}}}},"AktoAutoScalingGroup":{"Type":"AWS::AutoScaling::AutoScalingGroup","Properties":{"LaunchConfigurationName":{"Ref":"AktoASGLaunchConfiguration"},"VPCZoneIdentifier":[{"Ref":"SubnetId"}],"TargetGroupARNs":[{"Ref":"AktoTrafficMirroringTargetGroup"},{"Ref":"AktoKafkaTargetGroup"}],"MaxSize":"10","MinSize":"1"}},"AktoTargetTrackingNetworkPolicy":{"Type":"AWS::AutoScaling::ScalingPolicy","Properties":{"PolicyType":"TargetTrackingScaling","AutoScalingGroupName":{"Ref":"AktoAutoScalingGroup"},"EstimatedInstanceWarmup":30,"TargetTrackingConfiguration":{"PredefinedMetricSpecification":{"PredefinedMetricType":"ASGAverageNetworkIn"},"TargetValue":200000000}}},"AktoNLB":{"Type":"AWS::ElasticLoadBalancingV2::LoadBalancer","Properties":{"Type":"network","Scheme":"internal","IpAddressType":"ipv4","Subnets":[{"Ref":"SubnetId"}],"LoadBalancerAttributes":[{"Key":"load_balancing.cross_zone.enabled","Value":"true"}]}},"AktoTrafficMirroringTargetGroup":{"Type":"AWS::ElasticLoadBalancingV2::TargetGroup","Properties":{"Port":"4789","Protocol":"UDP","HealthCheckEnabled":"true","HealthCheckIntervalSeconds":10,"HealthCheckPath":"/metrics","HealthCheckPort":"8000","HealthCheckProtocol":"HTTP","HealthCheckTimeoutSeconds":6,"HealthyThresholdCount":2,"UnhealthyThresholdCount":2,"TargetType":"instance","VpcId":{"Fn::GetAtt":["CustomSourceGetVpcDetails","VpcId"]},"Targets":[]}},"AktoTrafficMirroringListener":{"Type":"AWS::ElasticLoadBalancingV2::Listener","Properties":{"LoadBalancerArn":{"Ref":"AktoNLB"},"Port":"4789","Protocol":"UDP","DefaultActions":[{"Type":"forward","TargetGroupArn":{"Ref":"AktoTrafficMirroringTargetGroup"}}]}},"AktoKafkaTargetGroup":{"Type":"AWS::ElasticLoadBalancingV2::TargetGroup","Properties":{"Port":"9092","Protocol":"TCP","TargetType":"instance","HealthCheckEnabled":"true","HealthCheckIntervalSeconds":10,"HealthCheckPath":"/metrics","HealthCheckPort":"8000","HealthCheckProtocol":"HTTP","HealthCheckTimeoutSeconds":6,"HealthyThresholdCount":2,"UnhealthyThresholdCount":2,"VpcId":{"Fn::GetAtt":["CustomSourceGetVpcDetails","VpcId"]},"Targets":[]}},"AktoKafkaListener":{"Type":"AWS::ElasticLoadBalancingV2::Listener","Properties":{"LoadBalancerArn":{"Ref":"AktoNLB"},"Port":"9092","Protocol":"TCP","DefaultActions":[{"Type":"forward","TargetGroupArn":{"Ref":"AktoKafkaTargetGroup"}}]}},"DashboardInstanceRefreshHandler":{"Type":"AWS::Lambda::Function","Properties":{"Handler":"index.handler","Runtime":"nodejs12.x","Timeout":30,"Role":{"Fn::GetAtt":["InstanceRefreshHandlerLambdaRole","Arn"]},"Code":{"ZipFile":"var aws = require('aws-sdk'); var autoscaling = new aws.AutoScaling(); exports.handler = function(event, context) {\n var params = {\n AutoScalingGroupName: 'AktoDashboardAutoScalingGroup', \n Preferences: {\n InstanceWarmup: 200, \n MinHealthyPercentage: 0\n }\n };\n \n autoscaling.startInstanceRefresh(params, function(err, data) {\n if(err) { console.log(err) }\n else { console.log(data) }\n })\n};\n"}}},"TrafficMirroringInstanceRefreshHandler":{"Type":"AWS::Lambda::Function","Properties":{"Handler":"index.handler","Runtime":"nodejs12.x","Timeout":30,"Role":{"Fn::GetAtt":["InstanceRefreshHandlerLambdaRole","Arn"]},"Code":{"ZipFile":"var aws = require('aws-sdk'); var autoscaling = new aws.AutoScaling(); exports.handler = function(event, context) {\n var params = {\n AutoScalingGroupName: 'AktoAutoScalingGroup', \n Preferences: {\n InstanceWarmup: 200, \n MinHealthyPercentage: 0\n }\n };\n \n autoscaling.startInstanceRefresh(params, function(err, data) {\n if(err) { console.log(err) }\n else { console.log(data) }\n })\n};\n"}}},"InstanceRefreshHandlerLambdaRole":{"Type":"AWS::IAM::Role","Properties":{"AssumeRolePolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":["lambda.amazonaws.com"]},"Action":["sts:AssumeRole"]}]},"Path":"/service-role/","Policies":[{"PolicyName":"lambdaExecution-DashboardInstanceRefreshHandler","PolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["logs:CreateLogGroup"],"Resource":"*"},{"Effect":"Allow","Action":["logs:CreateLogStream","logs:PutLogEvents"],"Resource":"*"},{"Effect":"Allow","Action":["autoscaling:StartInstanceRefresh","autoscaling:Describe*","autoscaling:UpdateAutoScalingGroup","ec2:CreateLaunchTemplateVersion","ec2:DescribeLaunchTemplates","ec2:RunInstances"],"Resource":"*"}]}}]}}},"Outputs":{"AktoNLB":{"Value":{"Ref":"AktoNLB"},"Description":"Arn of Akto Network Load Balancer"}}} \ No newline at end of file diff --git a/apps/dashboard/src/main/resources/google_client_secrets_01.json b/apps/dashboard/src/main/resources/google_client_secrets_01.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/apps/dashboard/src/main/resources/google_client_secrets_01.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/apps/dashboard/src/main/resources/libawesome.h b/apps/dashboard/src/main/resources/libawesome.h new file mode 100644 index 0000000000..040594bf36 --- /dev/null +++ b/apps/dashboard/src/main/resources/libawesome.h @@ -0,0 +1,75 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package command-line-arguments */ + + +#line 1 "cgo-builtin-export-prolog" + +#include /* for ptrdiff_t below */ + +#ifndef GO_CGO_EXPORT_PROLOGUE_H +#define GO_CGO_EXPORT_PROLOGUE_H + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +#endif + +#endif + +/* Start of preamble from import "C" comments. */ + + + + +/* End of preamble from import "C" comments. */ + + +/* Start of boilerplate cgo prologue. */ +#line 1 "cgo-gcc-export-header-prolog" + +#ifndef GO_CGO_PROLOGUE_H +#define GO_CGO_PROLOGUE_H + +typedef signed char GoInt8; +typedef unsigned char GoUint8; +typedef short GoInt16; +typedef unsigned short GoUint16; +typedef int GoInt32; +typedef unsigned int GoUint32; +typedef long long GoInt64; +typedef unsigned long long GoUint64; +typedef GoInt64 GoInt; +typedef GoUint64 GoUint; +typedef __SIZE_TYPE__ GoUintptr; +typedef float GoFloat32; +typedef double GoFloat64; +typedef float _Complex GoComplex64; +typedef double _Complex GoComplex128; + +/* + static assertion to make sure the file is being used on architecture + at least with matching size of GoInt. +*/ +typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef _GoString_ GoString; +#endif +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; + +#endif + +/* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void readTcpDumpFile(GoString filepath, GoString kafkaURL, GoInt apiCollectionId); + +#ifdef __cplusplus +} +#endif diff --git a/apps/dashboard/src/main/resources/libawesome.so b/apps/dashboard/src/main/resources/libawesome.so new file mode 100644 index 0000000000..9b54408299 Binary files /dev/null and b/apps/dashboard/src/main/resources/libawesome.so differ diff --git a/apps/dashboard/src/main/resources/struts.xml b/apps/dashboard/src/main/resources/struts.xml new file mode 100644 index 0000000000..1af2e67d6c --- /dev/null +++ b/apps/dashboard/src/main/resources/struts.xml @@ -0,0 +1,1529 @@ + + + + + + + + + + + + + + + /pages/login.jsp + + + + + + + {1} + + + + + + + akto_health + + + 401 + + + + + + + + + + + 401 + + + 423 + + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + subscription + + + 401 + + + + + + + + + 477 + + + 424 + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + newAccountId + + + 401 + + + + + + + + googleConfigResult + + + 401 + + + + + + + + + 401 + + + + + + + + driveNamesToThirdPartyId + + 401 + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + + code + + + + + + + + + apiCatalogData + + + 401 + + + + + + + + + + response + + + 401 + + + + + + + + + + response + + + 401 + + + + + + + + response + + + 401 + + + + + + + + + 401 + + + + + + + + response + + + 401 + + + + + + + + + response + + + 401 + + + + + + + + + response + + + 401 + + + + + + + + response + + + 401 + + + + + + + + response + + + 401 + + + + + + + + ret + + + 401 + + + + + + + + + ret + + + 401 + + + + + + + + + ret + + + 401 + + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + + 401 + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + + 401 + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + + 401 + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 401 + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + + 401 + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.*, ^responses.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + true + + + 401 + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + + 401 + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + true + + + 422 + false + ^actionErrors.* + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + stackState,availableLBs,dashboardHasNecessaryRole,isFirstSetup,selectedLBs + + + + 422 + false + ^actionErrors.* + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + stackState + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + endpoints + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + ^otp.* + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.*, ^responses.* + + + + + + + + + 422 + false + ^actionErrors.*, ^responses.* + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + ^token + + + 422 + false + ^actionErrors.* + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + 422 + false + ^actionErrors.*, ^responses.* + + + + + + + + + 422 + false + ^actionErrors.*, ^responses.* + + + + + + + + 422 + false + ^actionErrors.*, ^responses.* + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + + + + + 422 + false + ^actionErrors.* + + + + + + \ No newline at end of file diff --git a/apps/dashboard/src/main/resources/version.txt b/apps/dashboard/src/main/resources/version.txt new file mode 100644 index 0000000000..8eac1cf916 --- /dev/null +++ b/apps/dashboard/src/main/resources/version.txt @@ -0,0 +1,2 @@ +${akto-image-tag} +${akto-build-time} diff --git a/apps/dashboard/src/test/java/com/akto/action/ParseHar.java b/apps/dashboard/src/test/java/com/akto/action/ParseHar.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/dashboard/src/test/java/com/akto/action/TestApiCollectionsAction.java b/apps/dashboard/src/test/java/com/akto/action/TestApiCollectionsAction.java new file mode 100644 index 0000000000..2a0bd46dc2 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/TestApiCollectionsAction.java @@ -0,0 +1,133 @@ +package com.akto.action; + +import com.akto.MongoBasedTest; +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dto.ApiCollection; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.types.CappedSet; +import com.mongodb.BasicDBObject; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; + +public class TestApiCollectionsAction extends MongoBasedTest { + + private void validate(String name) { + List apiCollectionList = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + + if (name == null) { + assertEquals(apiCollectionList.size(), 0); + return; + } else { + assertEquals(apiCollectionList.size(), 1); + } + + assertEquals(apiCollectionList.get(0).getName(), name); + + } + + @Test + public void testHappy() { + ApiCollectionsDao.instance.getMCollection().drop(); + ApiCollectionsAction apiCollectionsAction = new ApiCollectionsAction(); + String name = "Avneesh.123-_"; + apiCollectionsAction.setCollectionName(name); + String result = apiCollectionsAction.createCollection(); + assertEquals(result, "SUCCESS"); + validate(name); + } + + @Test + public void testMaxSize() { + ApiCollectionsDao.instance.getMCollection().drop(); + ApiCollectionsAction apiCollectionsAction = new ApiCollectionsAction(); + String name = "Avneesh.123-_33333333333333333333333333333333333333333333"; + apiCollectionsAction.setCollectionName(name); + String result = apiCollectionsAction.createCollection(); + assertEquals(result, "ERROR"); + validate(null); + } + + @Test + public void testInvalidChars() { + ApiCollectionsDao.instance.getMCollection().drop(); + ApiCollectionsAction apiCollectionsAction = new ApiCollectionsAction(); + String name = "Avneesh#123"; + apiCollectionsAction.setCollectionName(name); + String result = apiCollectionsAction.createCollection(); + assertEquals(result, "ERROR"); + validate(null); + } + + @Test + public void testUniqueCollectionName() { + ApiCollectionsDao.instance.getMCollection().drop(); + ApiCollectionsAction apiCollectionsAction = new ApiCollectionsAction(); + String name = "Avneesh123"; + apiCollectionsAction.setCollectionName(name); + apiCollectionsAction.createCollection(); + validate(name); + + apiCollectionsAction.setCollectionName(name); + String result = apiCollectionsAction.createCollection(); + validate(name); + assertEquals(result, "ERROR"); + } + + @Test + public void fetchAllCollections() { + ApiCollectionsDao.instance.getMCollection().drop(); + List apiCollectionList = new ArrayList<>(); + + // mirroring collection with host + Set urls1 = new HashSet<>(Arrays.asList("1", "2", "3", "4", "5", "6")); + apiCollectionList.add(new ApiCollection(1000, "one", 1000, urls1, "one.com", 1000)); + + // mirroring collections without hosts + Set urls2 = new HashSet<>(Arrays.asList("1", "2", "3")); + apiCollectionList.add(new ApiCollection(2000, "two", 2000, urls2, null,2000)); + + // manually created collections + Set urls3 = new HashSet<>(Arrays.asList("1", "2", "3", "4")); + apiCollectionList.add(new ApiCollection(3000, "three", 3000, urls3, null,0)); + + ApiCollectionsDao.instance.insertMany(apiCollectionList); + + List singleTypeInfos = new ArrayList<>(); + for (int c=1; c<4; c++) { + for (int j = 0; j < 100; j++) { + for (int i = 0; i < 100; i++) { + int apiCollectionId = c*1000; + int responseCode = i % 2 == 0 ? -1 : 200; + String param = i == 0 ? "host" : "param_" + i; + boolean isHeader = i == 0; + String url = "url_" + j; + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId(url, "GET", responseCode, isHeader, param, SingleTypeInfo.GENERIC, apiCollectionId, false); + SingleTypeInfo sti = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 0, 0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + singleTypeInfos.add(sti); + } + } + } + + SingleTypeInfoDao.instance.insertMany(singleTypeInfos); + + ApiCollectionsAction apiCollectionsAction = new ApiCollectionsAction(); + apiCollectionsAction.fetchAllCollections(); + List apiCollections = apiCollectionsAction.apiCollections; + + assertEquals(3,apiCollections.size()); + + Map apiCollectionMap = new HashMap<>(); + for (ApiCollection apiCollection: apiCollections) { + apiCollectionMap.put(apiCollection.getId(), apiCollection); + } + + assertEquals(100, apiCollectionMap.get(1000).getUrlsCount()); + assertEquals(3, apiCollectionMap.get(2000).getUrlsCount()); // because burp collection we use count from urls stored in set + assertEquals(4, apiCollectionMap.get(3000).getUrlsCount()); // because burp collection we use count from urls stored in set + + } +} diff --git a/apps/dashboard/src/test/java/com/akto/action/TestCustomAuthTypeAction.java b/apps/dashboard/src/test/java/com/akto/action/TestCustomAuthTypeAction.java new file mode 100644 index 0000000000..37bbbbfabf --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/TestCustomAuthTypeAction.java @@ -0,0 +1,103 @@ +package com.akto.action; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import com.akto.MongoBasedTest; +import com.akto.dao.CustomAuthTypeDao; +import com.akto.dto.CustomAuthType; +import com.akto.dto.User; + +public class TestCustomAuthTypeAction extends MongoBasedTest { + + @Test + public void testFetchCustomAuthTypes(){ + CustomAuthTypeDao.instance.getMCollection().drop(); + List customAuthTypes = new ArrayList<>(); + customAuthTypes.add(new CustomAuthType("auth1", new ArrayList<>(Collections.singletonList("authtoken")),new ArrayList<>(Collections.singletonList("authtoken")), true, ACCOUNT_ID)); + customAuthTypes.add(new CustomAuthType("auth2", new ArrayList<>(Collections.singletonList("newauthtoken")),new ArrayList<>(Collections.singletonList("authtoken")), true, ACCOUNT_ID)); + CustomAuthTypeDao.instance.insertMany(customAuthTypes); + + CustomAuthTypeAction customAuthTypeAction = new CustomAuthTypeAction(); + + String result = customAuthTypeAction.fetchCustomAuthTypes(); + assertEquals("SUCCESS", result); + assertEquals(2, customAuthTypeAction.getCustomAuthTypes().size()); + } + + @Test + public void testAddCustomAuthType(){ + CustomAuthTypeDao.instance.getMCollection().drop(); + CustomAuthTypeAction customAuthTypeAction = new CustomAuthTypeAction(); + + Map session = new HashMap<>(); + User user = new User(); + user.setLogin("test@akto.io"); + session.put("user",user); + customAuthTypeAction.setSession(session); + + customAuthTypeAction.setName("auth1"); + customAuthTypeAction.setHeaderKeys(new ArrayList<>(Collections.singletonList("somekey"))); + customAuthTypeAction.setPayloadKeys(new ArrayList<>(Collections.singletonList("somekey"))); + customAuthTypeAction.setActive(true); + + String result = customAuthTypeAction.addCustomAuthType(); + assertEquals("SUCCESS", result); + assertEquals(1,customAuthTypeAction.getCustomAuthTypes().size()); + } + + @Test + public void testUpdateCustomAuthType(){ + CustomAuthTypeDao.instance.getMCollection().drop(); + CustomAuthType customAuthType = new CustomAuthType("auth1", new ArrayList<>(Collections.singletonList("authtoken")),new ArrayList<>(Collections.singletonList("authtoken")), true, ACCOUNT_ID); + CustomAuthTypeDao.instance.insertOne(customAuthType); + CustomAuthTypeAction customAuthTypeAction = new CustomAuthTypeAction(); + + Map session = new HashMap<>(); + User user = new User(); + user.setLogin("test@akto.io"); + user.setId(ACCOUNT_ID); + session.put("user",user); + customAuthTypeAction.setSession(session); + + customAuthTypeAction.setName("auth1"); + customAuthTypeAction.setHeaderKeys(new ArrayList<>(Collections.singletonList("somekey"))); + customAuthTypeAction.setPayloadKeys(new ArrayList<>(Collections.singletonList("somekey"))); + customAuthTypeAction.setActive(false); + + String result = customAuthTypeAction.updateCustomAuthType(); + assertEquals("SUCCESS", result); + assertEquals(false,customAuthTypeAction.getCustomAuthTypes().get(0).getActive()); + + } + + @Test + public void testUpdateCustomAuthTypeStatus(){ + CustomAuthTypeDao.instance.getMCollection().drop(); + CustomAuthType customAuthType = new CustomAuthType("auth1", new ArrayList<>(Collections.singletonList("authtoken")),new ArrayList<>(Collections.singletonList("authtoken")), true, ACCOUNT_ID); + CustomAuthTypeDao.instance.insertOne(customAuthType); + CustomAuthTypeAction customAuthTypeAction = new CustomAuthTypeAction(); + + Map session = new HashMap<>(); + User user = new User(); + user.setLogin("test@akto.io"); + user.setId(ACCOUNT_ID); + session.put("user",user); + customAuthTypeAction.setSession(session); + + customAuthTypeAction.setName("auth1"); + customAuthTypeAction.setActive(false); + + String result = customAuthTypeAction.updateCustomAuthTypeStatus(); + assertEquals("SUCCESS", result); + assertEquals(false,customAuthTypeAction.getCustomAuthTypes().get(0).getActive()); + + } +} diff --git a/apps/dashboard/src/test/java/com/akto/action/TestCustomDataTypeAction.java b/apps/dashboard/src/test/java/com/akto/action/TestCustomDataTypeAction.java new file mode 100644 index 0000000000..850edcce93 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/TestCustomDataTypeAction.java @@ -0,0 +1,246 @@ +package com.akto.action; + +import com.akto.DaoInit; +import com.akto.MongoBasedTest; +import com.akto.dao.CustomDataTypeDao; +import com.akto.dao.SampleDataDao; +import com.akto.dao.UsersDao; +import com.akto.dao.context.Context; +import com.akto.dto.CustomDataType; +import com.akto.dto.IgnoreData; +import com.akto.dto.User; +import com.akto.dto.data_types.Conditions; +import com.akto.dto.data_types.Predicate; +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.URLMethods; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.InsertOneModel; +import com.mongodb.client.model.WriteModel; +import org.junit.Test; + +import java.io.IOException; +import java.util.*; + +import static org.junit.Assert.*; + +public class TestCustomDataTypeAction extends MongoBasedTest { + + public static void main1(String[] args) throws Exception { + DaoInit.init(new ConnectionString("mongodb://172.18.0.2:27017/admini")); + Context.accountId.set(1_000_000); + ArrayList> bulkUpdates = new ArrayList<>(); + String payload = "{\"method\":\"GET\",\"requestPayload\":\"{\\\"includeCustom\\\":\\\"false\\\",\\\"resourceType\\\":\\\"Event\\\"}\",\"responsePayload\":\"{\\\"status\\\": \\\"ok\\\", \\\"results\\\": [{\\\"id\\\": 0, \\\"name\\\": \\\"$browser\\\", \\\"displayName\\\": \\\"Browser\\\", \\\"exampleValue\\\": \\\"Chrome, Safari\\\", \\\"description\\\": \\\"The browser being used at the time the event was fired. (Not versioned)\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$browser_version\\\", \\\"displayName\\\": \\\"Browser Version\\\", \\\"exampleValue\\\": \\\"11, 11.1\\\", \\\"description\\\": \\\"The version number of the browser being used at the time the event was fired.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$current_url\\\", \\\"displayName\\\": \\\"Current URL\\\", \\\"exampleValue\\\": \\\"https://mixpanel.com/example/\\\", \\\"description\\\": \\\"The full URL of the webpage on which the event is triggered.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$device_id\\\", \\\"displayName\\\": \\\"Device ID\\\", \\\"exampleValue\\\": \\\"\\\", \\\"description\\\": \\\"A default Mixpanel property to track the device as determined by Mixpanel's client-side SDKs. Events tracked from the same device and same user but different applications may not have the same $device_id. The reset method will change the device_id.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$initial_referrer\\\", \\\"displayName\\\": \\\"Initial Referrer\\\", \\\"exampleValue\\\": \\\"$direct, https://www.google.com/\\\", \\\"description\\\": \\\"The referring URL at first arrival.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$initial_referring_domain\\\", \\\"displayName\\\": \\\"Initial Referring Domain\\\", \\\"exampleValue\\\": \\\"$direct, www.google.com\\\", \\\"description\\\": \\\"The referring domain at first arrival.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$insert_id\\\", \\\"displayName\\\": \\\"Insert ID\\\", \\\"exampleValue\\\": \\\"5d958f87-542d-4c10-9422-0ed75893dc81\\\", \\\"description\\\": \\\"A random 36 character string of hyphenated alphanumeric characters that is unique to an event. Used to deduplicate events.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": true, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$lib_version\\\", \\\"displayName\\\": \\\"Library Version\\\", \\\"exampleValue\\\": \\\"5.4.1\\\", \\\"description\\\": \\\"Version of the Mixpanel library used to send this data.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$mp_api_endpoint\\\", \\\"displayName\\\": \\\"API Endpoint\\\", \\\"exampleValue\\\": \\\"api.mixpanel.com\\\", \\\"description\\\": \\\"Internal Mixpanel property to record the API endpoint the data was sent to\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": true, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$os\\\", \\\"displayName\\\": \\\"OS\\\", \\\"exampleValue\\\": \\\"Mac OS X, Windows\\\", \\\"description\\\": \\\"The operating system of the event sender.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$screen_height\\\", \\\"displayName\\\": \\\"Screen Height\\\", \\\"exampleValue\\\": \\\"960\\\", \\\"description\\\": \\\"The height of a device's screen.\\\", \\\"type\\\": \\\"number\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$screen_width\\\", \\\"displayName\\\": \\\"Screen Width\\\", \\\"exampleValue\\\": \\\"360\\\", \\\"description\\\": \\\"The width of the screen of the device.\\\", \\\"type\\\": \\\"number\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"mp_country_code\\\", \\\"displayName\\\": \\\"Country\\\", \\\"exampleValue\\\": \\\"United States, Canada\\\", \\\"description\\\": \\\"The country of the event sender, parsed from IP.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"mp_lib\\\", \\\"displayName\\\": \\\"Mixpanel Library\\\", \\\"exampleValue\\\": \\\"Web, Android, iPhone\\\", \\\"description\\\": \\\"The name of the Mixpanel Library that sent the event.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"mp_processing_time_ms\\\", \\\"displayName\\\": \\\"Time Processed (UTC)\\\", \\\"exampleValue\\\": \\\"1543546310165\\\", \\\"description\\\": \\\"Time in milliseconds in Unix Time Stamp when an event was ingested by Mixpanel. While the \\\\\\\"Time\\\\\\\" property in Mixpanel is set in the time zone specified by the project, mp_processing_time_ms will always be in UTC (GMT) time.\\\", \\\"type\\\": \\\"number\\\", \\\"hidden\\\": true, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$event_name\\\", \\\"displayName\\\": \\\"Event Name\\\", \\\"exampleValue\\\": \\\"Song Played\\\", \\\"description\\\": \\\"Name of the event\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$region\\\", \\\"displayName\\\": \\\"Region\\\", \\\"exampleValue\\\": \\\"California, New York\\\", \\\"description\\\": \\\"The region (state or province) of the event sender that is parsed from IP.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3746, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$city\\\", \\\"displayName\\\": \\\"City\\\", \\\"exampleValue\\\": \\\"San Francisco, Los Angeles\\\", \\\"description\\\": \\\"The city of the event sender, parsed from IP.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3740, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 3}, {\\\"id\\\": 0, \\\"name\\\": \\\"$referrer\\\", \\\"displayName\\\": \\\"Referrer\\\", \\\"exampleValue\\\": \\\"https://mixpanel.com/example, https://mixpanel.com/example/segmentation\\\", \\\"description\\\": \\\"The referring URL, including your own domain.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 2443, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$referring_domain\\\", \\\"displayName\\\": \\\"Referring Domain\\\", \\\"exampleValue\\\": \\\"www.google.com, www.mixpanel.com\\\", \\\"description\\\": \\\"The referring domain, including your own domain.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 2443, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$search_engine\\\", \\\"displayName\\\": \\\"Search Engine\\\", \\\"exampleValue\\\": \\\"Google, Yahoo\\\", \\\"description\\\": \\\"The search engine that was used to arrive at your domain.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 5, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$device\\\", \\\"displayName\\\": \\\"Device\\\", \\\"exampleValue\\\": \\\"Android, iPhone\\\", \\\"description\\\": \\\"The name of the event sender's device, if they're on mobile web.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 1, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$duration_s\\\", \\\"displayName\\\": \\\"Session Duration (Seconds)\\\", \\\"exampleValue\\\": \\\"413\\\", \\\"description\\\": \\\"The duration between Session Start and Session End events in seconds.\\\", \\\"type\\\": \\\"number\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$event_count\\\", \\\"displayName\\\": \\\"Session Event Count\\\", \\\"exampleValue\\\": \\\"103\\\", \\\"description\\\": \\\"The number of events during a session. This does not include Excluded Events and Hidden Events in Lexicon.\\\", \\\"type\\\": \\\"number\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$origin_end\\\", \\\"displayName\\\": \\\"Session End Event Name\\\", \\\"exampleValue\\\": \\\"Sign Out, Complete Purchase,\\\", \\\"description\\\": \\\"The original event name that triggered Session End event.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$origin_start\\\", \\\"displayName\\\": \\\"Session Start Event Name\\\", \\\"exampleValue\\\": \\\"Sign In, View Pricing, Landing Page\\\", \\\"description\\\": \\\"The original event name that triggered Session Start event.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$distinct_id\\\", \\\"displayName\\\": \\\"Distinct ID\\\", \\\"exampleValue\\\": \\\"166424e227e7fb-0ff2f469b2f1fc-2d6a4f35\\\", \\\"description\\\": \\\"The way to uniquely identify your users. You can set this to any value using the identify method. Some Mixpanel libraries assign a random value by default.\\\", \\\"type\\\": \\\"string\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isSensitiveBlacklisted\\\": true, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}, {\\\"id\\\": 0, \\\"name\\\": \\\"$time\\\", \\\"displayName\\\": \\\"Time\\\", \\\"exampleValue\\\": \\\"2011-01T00:00:00Z\\\", \\\"description\\\": \\\"A unix time epoch that is used to determine the time of an event. If no time property is provided, we will use the time the event arrives at our servers.\\\", \\\"type\\\": \\\"datetime\\\", \\\"hidden\\\": false, \\\"dropped\\\": false, \\\"merged\\\": false, \\\"resourceType\\\": \\\"Event\\\", \\\"mergedPropertyId\\\": 0, \\\"status\\\": \\\"live\\\", \\\"sensitive\\\": false, \\\"required\\\": false, \\\"entityDefinitionId\\\": 0, \\\"isMixpanelDefinition\\\": true, \\\"count\\\": 3830, \\\"hasArbData\\\": true, \\\"uiUniqueQueryCount\\\": 0, \\\"uiQueryCount\\\": 0, \\\"apiQueryCount\\\": 0}]}\",\"ip\":\"null\",\"source\":\"HAR\",\"type\":\"HTTP/2\",\"akto_vxlan_id\":\"1648918173\",\"path\":\"https://mixpanel.com/api/app/workspaces/3138707/data-definitions/properties\",\"requestHeaders\":\"{\\\"Cookie\\\":\\\"mp__origin=invite; mp__origin_referrer=\\\\\\\"https://mixpanel.com/register/?company=Akto&name=&email=avneesh%40akto.io&nonce=c49093b12c5e7141a5ff13cca87c1a46c05fdf84a1d441d5c8f4e5a8fed2b995&next=%2Forganizations%2F2162460%2Faccept-invite%2F1edf5694b07504adc6f5fc8799d00cc790183d51d6b6d965fac00a93be70c248%2F1639393348%2Fc49093b12c5e7141a5ff13cca87c1a46c05fdf84a1d441d5c8f4e5a8fed2b995&invited=1&inviter=Ankita+Gupta\\\\\\\"; mixpanel_utm_medium=direct; mixpanel_utm_source=mixpanel; mixpanel_utm_campaign=https%3A//mixpanel.com/register/%3Fcompany%3DAkto%26name%3D%26email%3Davneesh%2540akto.io%26nonce%3Dc49093b12c5e7141a5ff13cca87c1a46c05fdf84a1d441d5c8f4e5a8fed2b995%26next%3D%252Forganizations%252F2162460%252Faccept-invite%252F1edf5694b07504adc6f5fc8799d00cc790183d51d6b6d965fac00a93be7; mixpanel_utm_content=https%3A//mixpanel.com; mixpanel_utm_term=na; g_state={\\\\\\\"i_l\\\\\\\":0}; mp_user=\\\\\\\"eyJpZCI6MjgzNTUwMSwibmFtZSI6IkF2bmVlc2ggSG90YSIsImVtYWlsIjoiYXZuZWVzaEBha3RvLmlvIn0=\\\\\\\"; csrftoken=k66J92L57AxjLwUNdIKrRFkscXTORuDCQ0DhgFN1hghPQfcf9Q0xf0Xu9naR9l40; sessionid=wo4icgm89xcwsjrrvmc8plsl424787uf; mp_distinct_id=2835501; mp_persistence=%7B%22project%22%3A2599843%2C%22workspace%22%3A3138707%2C%22report_path%22%3A%22dashboards%22%7D; mp_account_id=2811172\\\",\\\"Accept\\\":\\\"*/*\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0\\\",\\\"Referer\\\":\\\"https://mixpanel.com/project/2599843/view/3138707/app/dashboards\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"mixpanel.com\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Pragma\\\":\\\"no-cache\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"Authorization\\\":\\\"Session\\\",\\\"TE\\\":\\\"trailers\\\",\\\"Cache-Control\\\":\\\"no-cache\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"Mon, 28 Feb 2022 06:37:02 GMT\\\",\\\"server\\\":\\\"nginx\\\",\\\"set-cookie\\\":\\\"mp_user=\\\\\\\"eyJpZCI6MjgzNTUwMSwibmFtZSI6IkF2bmVlc2ggSG90YSIsImVtYWlsIjoiYXZuZWVzaEBha3RvLmlvIn0=\\\\\\\"; expires=Mon, 14 Mar 2022 06:37:02 GMT; Max-Age=1209600; Path=/\\\",\\\"vary\\\":\\\"Authorization, Cookie\\\",\\\"x-frame-options\\\":\\\"SAMEORIGIN\\\",\\\"content-encoding\\\":\\\"gzip\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"x-server-elapsed\\\":\\\"5.420\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"strict-transport-security\\\":\\\"max-age=63072000; includeSubDomains; preload\\\",\\\"x-mp-request-id\\\":\\\"fc5639ae-93ff-4697-85ca-2398d0b59b08\\\"}\",\"time\":\"1646030216\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + + + for (int i=0; i < 10000; i++) { + SampleData sampleData = new SampleData( + new Key(i,"url", URLMethods.Method.GET, 200, 0,0), Arrays.asList(payload, payload, payload, payload, payload, payload, payload, payload, payload, payload) + ); + bulkUpdates.add(new InsertOneModel<>(sampleData)); + } + + SampleDataDao.instance.getMCollection().bulkWrite(bulkUpdates); + } + + private CustomDataTypeAction generateCustomDataTypeAction( + String name, String operator, List keyConditions, + List valueConditions + ) { + CustomDataTypeAction customDataTypeAction = new CustomDataTypeAction(); + Map session = new HashMap<>(); + session.put("user", new User()); + customDataTypeAction.setSession(session); + customDataTypeAction.setCreateNew(true); + + customDataTypeAction.setName(name); + customDataTypeAction.setSensitiveAlways(true); + customDataTypeAction.setOperator(operator); + + customDataTypeAction.setKeyOperator("OR"); + customDataTypeAction.setKeyConditionFromUsers(keyConditions); + + customDataTypeAction.setValueOperator("AND"); + customDataTypeAction.setValueConditionFromUsers(valueConditions); + + return customDataTypeAction; + } + + + @Test + public void testGenerateCustomDataTypeHappy() { + Context.accountId.set(1_000_000); + CustomDataTypeDao.instance.getMCollection().drop(); + Map valueMap = new HashMap<>(); + valueMap.put("value", "wencludeCustom"); + CustomDataTypeAction customDataTypeAction = generateCustomDataTypeAction( + "test_1", "AND", + Arrays.asList(new CustomDataTypeAction.ConditionFromUser(Predicate.Type.STARTS_WITH, valueMap), new CustomDataTypeAction.ConditionFromUser(Predicate.Type.ENDS_WITH, valueMap)), + Collections.singletonList(new CustomDataTypeAction.ConditionFromUser(Predicate.Type.REGEX, valueMap))); + + customDataTypeAction.execute(); + assertNotNull(customDataTypeAction.getCustomDataType()); + + CustomDataType customDataType = CustomDataTypeDao.instance.findOne(Filters.eq(CustomDataType.NAME, "TEST_1")); + + assertEquals(customDataType.getCreatorId(), 0); + assertEquals(customDataType.getName(), "TEST_1"); + assertTrue(customDataType.isSensitiveAlways()); + assertEquals(customDataType.getOperator(), Conditions.Operator.AND); + assertEquals(customDataType.getKeyConditions().getOperator(), Conditions.Operator.OR); + assertEquals(customDataType.getKeyConditions().getPredicates().size(), 2); + assertEquals(customDataType.getValueConditions().getOperator(), Conditions.Operator.AND); + assertEquals(customDataType.getValueConditions().getPredicates().size(), 1); + assertEquals(customDataType.getCreatorId(), 0); + + // updating + customDataTypeAction.setActive(false); + customDataTypeAction.setKeyOperator("AND"); + customDataTypeAction.setValueOperator("OR"); + customDataTypeAction.setKeyConditionFromUsers(Collections.emptyList()); + customDataTypeAction.setCreateNew(false); + + customDataTypeAction.execute(); + assertFalse(customDataTypeAction.getCustomDataType().isActive()); + + customDataType = CustomDataTypeDao.instance.findOne(Filters.eq(CustomDataType.NAME, "TEST_1")); + + assertEquals(customDataType.getCreatorId(), 0); + assertEquals(customDataType.getName(), "TEST_1"); + assertTrue(customDataType.isSensitiveAlways()); + assertFalse(customDataType.isActive()); + assertEquals(customDataType.getOperator(), Conditions.Operator.AND); + assertNull(customDataType.getKeyConditions()); + assertEquals(customDataType.getValueConditions().getOperator(), Conditions.Operator.OR); + assertEquals(customDataType.getValueConditions().getPredicates().size(), 1); + assertEquals(customDataType.getCreatorId(), 0); + + + // update invalid name + customDataTypeAction.setName("RANDOM_SHIT"); + customDataTypeAction.execute(); + assertNull(customDataTypeAction.getCustomDataType()); + + } + + + @Test + public void testGenerateCustomDataTypeNewInvalidName() { + Map valueMap = new HashMap<>(); + valueMap.put("value", "wencludeCustom"); + CustomDataTypeAction customDataTypeAction = generateCustomDataTypeAction( + "", "AND", + Arrays.asList(new CustomDataTypeAction.ConditionFromUser(Predicate.Type.STARTS_WITH, valueMap), new CustomDataTypeAction.ConditionFromUser(Predicate.Type.ENDS_WITH, valueMap)), + Collections.singletonList(new CustomDataTypeAction.ConditionFromUser(Predicate.Type.REGEX, valueMap))); + + customDataTypeAction.execute(); + assertNull(customDataTypeAction.getCustomDataType()); + + customDataTypeAction.setName("Avneesh.hota"); + customDataTypeAction.execute(); + assertNull(customDataTypeAction.getCustomDataType()); + } + + @Test + public void testGenerateCustomDataTypeNewInvalidOperator() { + Map valueMap = new HashMap<>(); + valueMap.put("value", "wencludeCustom"); + CustomDataTypeAction customDataTypeAction = generateCustomDataTypeAction( + "first", "something", + Arrays.asList(new CustomDataTypeAction.ConditionFromUser(Predicate.Type.STARTS_WITH, valueMap), new CustomDataTypeAction.ConditionFromUser(Predicate.Type.ENDS_WITH, valueMap)), + Collections.singletonList(new CustomDataTypeAction.ConditionFromUser(Predicate.Type.REGEX, valueMap))); + + customDataTypeAction.execute(); + + assertNull(customDataTypeAction.getCustomDataType()); + } + + @Test + public void testGenerateCustomDataTypeNewInvalidValueMap() { + Map valueMap = new HashMap<>(); + valueMap.put("valude", "wencludeCustom"); + CustomDataTypeAction customDataTypeAction = generateCustomDataTypeAction( + "first", "AND", + Arrays.asList(new CustomDataTypeAction.ConditionFromUser(Predicate.Type.STARTS_WITH, valueMap), new CustomDataTypeAction.ConditionFromUser(Predicate.Type.ENDS_WITH, valueMap)), + Collections.singletonList(new CustomDataTypeAction.ConditionFromUser(Predicate.Type.REGEX, valueMap))); + + customDataTypeAction.execute(); + + assertNull(customDataTypeAction.getCustomDataType()); + } + + + @Test + public void testFetchDataTypes() { + IgnoreData ignoreData = new IgnoreData(new HashMap<>(), new HashSet<>()); + CustomDataType customDataType1 = new CustomDataType("name1", true, Collections.emptyList(), 1, true, null,null, Conditions.Operator.AND,ignoreData); + User user1 = new User(); + user1.setId(1); + user1.setName("user1"); + + CustomDataType customDataType2 = new CustomDataType("name2", true, Collections.emptyList(), 2,false, null,null, Conditions.Operator.AND,ignoreData); + User user2 = new User(); + user2.setId(2); + user2.setName("user1"); + + CustomDataType customDataType3 = new CustomDataType("name3", true, Collections.emptyList(), 3, true, null,null, Conditions.Operator.AND,ignoreData); + User user3 = new User(); + user3.setId(3); + user3.setName("user1"); + + User user4 = new User(); + user4.setId(4); + user4.setName("user4"); + + User user5 = new User(); + user5.setId(5); + user5.setName("user5"); + + UsersDao.instance.getMCollection().drop(); + CustomDataTypeDao.instance.getMCollection().drop(); + + UsersDao.instance.insertMany(Arrays.asList(user1, user2,user3, user4, user5)); + CustomDataTypeDao.instance.insertMany(Arrays.asList(customDataType1, customDataType2, customDataType3)); + + CustomDataTypeAction customDataTypeAction = new CustomDataTypeAction(); + Map session = new HashMap<>(); + session.put("user", user4); + customDataTypeAction.setSession(session); + customDataTypeAction.fetchDataTypesForSettings(); + + BasicDBObject dataTypes = customDataTypeAction.getDataTypes(); + + assertEquals(((List) dataTypes.get("customDataTypes")).size(), 3); + assertEquals(((Map) dataTypes.get("usersMap")).size(), 4); + + } + + @Test + public void testToggleDataTypeActiveParam() { + Context.accountId.set(1_000_000); + CustomDataTypeDao.instance.getMCollection().drop(); + IgnoreData ignoreData = new IgnoreData(new HashMap<>(), new HashSet<>()); + CustomDataType customDataType = new CustomDataType("NAME1", true, Collections.emptyList(), 1, true, null,null, Conditions.Operator.AND,ignoreData); + CustomDataTypeDao.instance.insertOne(customDataType); + + CustomDataTypeAction customDataTypeAction = new CustomDataTypeAction(); + Map session = new HashMap<>(); + session.put("user", new User()); + customDataTypeAction.setSession(session); + customDataTypeAction.setActive(false); + customDataTypeAction.setName("NAME1"); + + customDataTypeAction.toggleDataTypeActiveParam(); + assertFalse(customDataTypeAction.getCustomDataType().isActive()); + + CustomDataType customDataTypeFromDb = CustomDataTypeDao.instance.findOne(CustomDataType.NAME, "NAME1"); + assertFalse(customDataTypeFromDb.isActive()); + } + +} diff --git a/apps/dashboard/src/test/java/com/akto/action/TestExportSampleDataAction.java b/apps/dashboard/src/test/java/com/akto/action/TestExportSampleDataAction.java new file mode 100644 index 0000000000..5f546808d6 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/TestExportSampleDataAction.java @@ -0,0 +1,27 @@ +package com.akto.action; + +import org.junit.Test; +import static org.junit.Assert.assertNotNull; + +import java.io.IOException; + +public class TestExportSampleDataAction { + + @Test + public void curlEmptyRequestBodyGet() { + String sampleData = "{\"akto_account_id\":\"1000000\",\"akto_vxlan_id\":\"280483\",\"ip\":\"172.31.5.42\",\"is_pending\":\"false\",\"method\":\"GET\",\"path\":\"/api/books\",\"requestHeaders\":\"{\\\"Host\\\":\\\"akto.io\\\",\\\"Accept-Encoding\\\":\\\"gzip, compressed\\\",\\\"Connection\\\":\\\"close\\\",\\\"User-Agent\\\":\\\"ELB-HealthChecker/2.0\\\"}\",\"requestPayload\":\"\",\"responseHeaders\":\"{\\\"Content-Length\\\":\\\"116\\\",\\\"Content-Type\\\":\\\"application/json;charset=utf-8\\\",\\\"Date\\\":\\\"Fri, 04 Mar 2022 18:42:21 GMT\\\"}\",\"responsePayload\":\"{\\\"id\\\":\\\"1\\\",\\\"isbn\\\":\\\"3223\\\",\\\"title\\\":\\\"Book 1\\\",\\\"author\\\":{\\\"firstname\\\":\\\"Avneesh\\\",\\\"lastname\\\":\\\"Hota\\\"},\\\"timestamp\\\":1646416193}\\n\",\"status\":\"201 Created\",\"statusCode\":\"201\",\"time\":\"1646419341\",\"type\":\"HTTP/1.1\"}"; + ExportSampleDataAction exportSampleDataAction = new ExportSampleDataAction(); + exportSampleDataAction.setSampleData(sampleData); + exportSampleDataAction.generateCurl(); + assertNotNull(exportSampleDataAction.getCurlString()); + } + + @Test + public void curlEmptyRequestBodyPost() { + String sampleData = "{\"akto_account_id\":\"1000000\",\"akto_vxlan_id\":\"280483\",\"ip\":\"172.31.5.42\",\"is_pending\":\"true\",\"method\":\"POST\",\"path\":\"/api/cars\",\"requestHeaders\":\"{\\\"Accept\\\":\\\"*/*\\\",\\\"User-Agent\\\":\\\"curl/7.79.1\\\",\\\"X-Amzn-Trace-Id\\\":\\\"Root=1-6222559f-33297d9f42d25b854217ca32\\\",\\\"X-Forwarded-For\\\":\\\"172.31.11.244\\\",\\\"X-Forwarded-Port\\\":\\\"443\\\",\\\"X-Forwarded-Proto\\\":\\\"https\\\"}\",\"requestPayload\":\"\",\"responseHeaders\":\"{\\\"Content-Length\\\":\\\"116\\\",\\\"Content-Type\\\":\\\"application/json;charset=utf-8\\\",\\\"Date\\\":\\\"Fri, 04 Mar 2022 18:08:31 GMT\\\"}\",\"responsePayload\":\"{\\\"id\\\":\\\"1\\\",\\\"isbn\\\":\\\"3223\\\",\\\"title\\\":\\\"Book 1\\\",\\\"author\\\":{\\\"firstname\\\":\\\"Avneesh\\\",\\\"lastname\\\":\\\"Hota\\\"},\\\"timestamp\\\":1646416193}\\n\",\"status\":\"201 Created\",\"statusCode\":\"201\",\"time\":\"1646417399\",\"type\":\"HTTP/1.1\"}"; + ExportSampleDataAction exportSampleDataAction = new ExportSampleDataAction(); + exportSampleDataAction.setSampleData(sampleData); + exportSampleDataAction.generateCurl(); + assertNotNull(exportSampleDataAction.getCurlString()); + } +} diff --git a/apps/dashboard/src/test/java/com/akto/action/TestIgnoreFalsePositivesAction.java b/apps/dashboard/src/test/java/com/akto/action/TestIgnoreFalsePositivesAction.java new file mode 100644 index 0000000000..255e1fc435 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/TestIgnoreFalsePositivesAction.java @@ -0,0 +1,51 @@ +package com.akto.action; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.Test; + +import com.akto.MongoBasedTest; +import com.akto.dao.AktoDataTypeDao; +import com.akto.dto.AktoDataType; +import com.akto.dto.IgnoreData; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.SingleTypeInfo.ParamId; +import static org.junit.Assert.assertEquals; + +public class TestIgnoreFalsePositivesAction extends MongoBasedTest{ + + @Test + public void test(){ + AktoDataTypeDao.instance.getMCollection().drop(); + AktoDataType aktoDataType = new AktoDataType("UUID", false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>())); + Set ignored = new HashSet<>(); + ignored.add("something"); + aktoDataType.getIgnoreData().setIgnoredKeysInAllAPIs(ignored); + AktoDataTypeDao.instance.insertOne(aktoDataType); + IgnoreFalsePositivesAction ignoreTest = new IgnoreFalsePositivesAction(); + Map falsePositives = new HashMap<>(); + IgnoreData ignoreData = new IgnoreData(); + ignored.add("somethingelse"); + ignoreData.setIgnoredKeysInAllAPIs(ignored); + Map> ignoredKeysInSelectedAPIs = new HashMap<>(); + ParamId paramId = new ParamId("api/STRING", "GET", -1, false, "someveryrandomUUID", SingleTypeInfo.UUID, 0, false); + List paramIds = new ArrayList<>(); + paramIds.add(paramId); + ignoredKeysInSelectedAPIs.put("someveryrandomUUID", paramIds); + ignoreData.setIgnoredKeysInSelectedAPIs(ignoredKeysInSelectedAPIs); + falsePositives.put("UUID",ignoreData); + ignoreTest.setFalsePositives(falsePositives); + SingleTypeInfo.aktoDataTypeMap.put("UUID",aktoDataType); + + String result = ignoreTest.setFalsePositivesInSensitiveData(); + aktoDataType = AktoDataTypeDao.instance.findOne("name","UUID"); + assertEquals("SUCCESS",result); + assertEquals(2,aktoDataType.getIgnoreData().getIgnoredKeysInAllAPIs().size()); + assertEquals(1,aktoDataType.getIgnoreData().getIgnoredKeysInSelectedAPIs().size()); + } +} \ No newline at end of file diff --git a/apps/dashboard/src/test/java/com/akto/action/TestParamStateAction.java b/apps/dashboard/src/test/java/com/akto/action/TestParamStateAction.java new file mode 100644 index 0000000000..46f4f45069 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/TestParamStateAction.java @@ -0,0 +1,59 @@ +package com.akto.action; + +import com.akto.MongoBasedTest; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.types.CappedSet; +import com.mongodb.client.model.UpdateOneModel; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.Updates; +import com.mongodb.client.model.WriteModel; +import org.bson.conversions.Bson; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class TestParamStateAction extends MongoBasedTest { + + private UpdateOneModel buildSingleTypeInfo(int publicCount, int uniqueCount, boolean isUrlParam, String param) { + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + "url1", "GET", -1, false, param, SingleTypeInfo.GENERIC,0, isUrlParam + ); + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 0,0,0, new CappedSet<>(), SingleTypeInfo.Domain.ANY, 0,0); + singleTypeInfo.setUniqueCount(uniqueCount); + singleTypeInfo.setPublicCount(publicCount); + + Bson updateKey = SingleTypeInfoDao.createFilters(singleTypeInfo); + Bson update = Updates.combine( + Updates.set(SingleTypeInfo._UNIQUE_COUNT, uniqueCount), + Updates.set(SingleTypeInfo._PUBLIC_COUNT, publicCount) + ); + return new UpdateOneModel<>(updateKey, update, new UpdateOptions().upsert(true)); + } + + @Test + public void testFetchParamsStatus() { + SingleTypeInfoDao.instance.getMCollection().drop(); + ArrayList> bulkUpdates = new ArrayList<>(); + + bulkUpdates.add( buildSingleTypeInfo(10, 20, false, "param1")); + bulkUpdates.add( buildSingleTypeInfo(10, 200, false, "param2")); + bulkUpdates.add( buildSingleTypeInfo(0, 0, false, "param3")); + + bulkUpdates.add( buildSingleTypeInfo(10, 20,true, "1")); + bulkUpdates.add( buildSingleTypeInfo(10, 200,true, "2")); + bulkUpdates.add( buildSingleTypeInfo(0, 0,true, "3")); + + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkUpdates); + + ParamStateAction paramStateAction = new ParamStateAction(); + String result = paramStateAction.fetchParamsStatus(); + assertEquals("SUCCESS", result); + + assertEquals(2, paramStateAction.getPrivateSingleTypeInfo().size()); + } +} diff --git a/apps/dashboard/src/test/java/com/akto/action/TestSignupAction.java b/apps/dashboard/src/test/java/com/akto/action/TestSignupAction.java new file mode 100644 index 0000000000..d7163e189b --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/TestSignupAction.java @@ -0,0 +1,30 @@ +package com.akto.action; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class TestSignupAction { + + @Test + public void testValidatePassword() { + String code = SignupAction.validatePassword("avneesh"); + assertEquals(code, SignupAction.MINIMUM_PASSWORD_ERROR); + + code = SignupAction.validatePassword("avneesh_avneesh_avneesh_avneesh_avneesh_avneesh_avneesh_avneesh_avneesh"); + assertEquals(code, SignupAction.MAXIMUM_PASSWORD_ERROR); + + code = SignupAction.validatePassword("avneesh\uD83D\uDE03123"); + assertEquals(code, SignupAction.INVALID_CHAR); + + code = SignupAction.validatePassword("avneesh_avneesh"); + assertEquals(code, SignupAction.MUST_BE_ALPHANUMERIC_ERROR); + + code = SignupAction.validatePassword("avneesh.12345"); + assertNull(code); + + code = SignupAction.validatePassword("hotavneesh1"); + assertNull(code); + } +} diff --git a/apps/dashboard/src/test/java/com/akto/action/TestSwaggerData.java b/apps/dashboard/src/test/java/com/akto/action/TestSwaggerData.java new file mode 100644 index 0000000000..95975c67e8 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/TestSwaggerData.java @@ -0,0 +1,112 @@ +package com.akto.action; + +import com.akto.action.observe.InventoryAction; +import com.mongodb.BasicDBObject; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.servers.Server; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class TestSwaggerData { + + private Server generateServer(String url) { + Server server = new Server(); + server.setUrl(url); + return server; + } + + private Paths generatePaths(List> values) { + Paths paths = new Paths(); + + for (List v: values) { + PathItem pathItem = paths.get(v.get(0)); + if (pathItem == null) pathItem = new PathItem(); + if (Objects.equals(v.get(1), "GET")) { + pathItem.setGet(new Operation()); + } else if (Objects.equals(v.get(1), "POST")) { + pathItem.setPost(new Operation()); + } else if (Objects.equals(v.get(1), "OPTIONS")) { + pathItem.setOptions(new Operation()); + } else { + pathItem.setPatch(new Operation()); + } + paths.addPathItem(v.get(0), pathItem); + } + + return paths; + } + + @Test + public void happy() { + OpenAPI openAPI = new OpenAPI(); + + openAPI.setServers(new ArrayList<>()); + openAPI.getServers().add(generateServer("https://www.akto.io/")); + openAPI.getServers().add(generateServer("https://www.akto.io/v1")); + + List> values = new ArrayList<>(); + values.add(Arrays.asList("/api/books", "GET")); + values.add(Arrays.asList("/api/books", "POST")); + values.add(Arrays.asList("/api/books", "OPTIONS")); // unused + values.add(Arrays.asList("/api/books/{book_Id}", "GET")); + values.add(Arrays.asList("/api/books/{book_Id}", "OPTIONS")); // unused + values.add(Arrays.asList("/api/cars", "GET")); + values.add(Arrays.asList("/api/bus", "GET")); + values.add(Arrays.asList("/api/unused1", "GET")); // unused + values.add(Arrays.asList("/api/unused1/{random}", "GET")); // unused + values.add(Arrays.asList("/api/used2", "GET")); + + openAPI.setPaths(generatePaths(values)); + + List endpoints = new ArrayList<>(); + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","/api/books").append("method", "GET"))); + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","/api/books").append("method", "POST"))); + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","/api/books").append("method", "PATCH"))); // shadow + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","/api/somethingRandom").append("method", "GET"))); //shadow + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","/v1/api/books").append("method", "GET"))); + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","/api/books/INTEGER").append("method", "GET"))); + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","/v1/api/books/INTEGER").append("method", "GET"))); + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","/api/cars").append("method", "GET"))); + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","/api/cars/STRING").append("method", "GET"))); //shadow + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","https://www.akto.io/api/bus").append("method", "GET"))); + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","https://www.akto.io/v1/api/bus").append("method", "GET"))); + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","https://www.akto.io/xyz/api/bus").append("method", "GET"))); // shadow + endpoints.add(new BasicDBObject().append("_id",new BasicDBObject().append("url","/v1/api/used2").append("method", "GET"))); + + + Set unused = InventoryAction.fetchSwaggerData(endpoints, openAPI); + // 2*10 endpoints in swagger file (since 2 servers) + // Then subtract the non-shadow endpoints from total + assertEquals(unused.size(), 11); + + int counter = 0; + for (BasicDBObject endpoint: endpoints) { + if (endpoint.get("shadow") != null && (boolean) endpoint.get("shadow")) { + counter ++; + } + } + assertEquals(counter, 4); + + } + + @Test + public void testRetrievePath() { + String path = InventoryAction.retrievePath("/"); + assertEquals(path, ""); + + path = InventoryAction.retrievePath("https://www.google.com/avneesh"); + assertEquals(path, "/avneesh"); + + path = InventoryAction.retrievePath("https://www.google.com/"); + assertEquals(path, ""); + + path = InventoryAction.retrievePath("https://www.google.com"); + assertEquals(path, ""); + } +} diff --git a/apps/dashboard/src/test/java/com/akto/action/TestWebhookAction.java b/apps/dashboard/src/test/java/com/akto/action/TestWebhookAction.java new file mode 100644 index 0000000000..97f512b3e4 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/TestWebhookAction.java @@ -0,0 +1,167 @@ +package com.akto.action; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import com.akto.MongoBasedTest; +import com.akto.dao.notifications.CustomWebhooksDao; +import com.akto.dao.notifications.CustomWebhooksResultDao; +import com.akto.dto.User; +import com.akto.dto.notifications.CustomWebhook; +import com.akto.dto.notifications.CustomWebhookResult; +import com.akto.dto.notifications.CustomWebhook.ActiveStatus; + +import com.akto.dto.type.URLMethods.Method; +import com.mongodb.BasicDBObject; + +public class TestWebhookAction extends MongoBasedTest{ + + + @Test + public void testFetchCustomWebhooks(){ + CustomWebhooksDao.instance.getMCollection().drop(); + CustomWebhook customWebhook = new CustomWebhook(1,"webhook name","http://test.akto.io","","queryParam=test","body",Method.POST,10,"test@akto.io",0,0,0,ActiveStatus.ACTIVE); + CustomWebhook customWebhook2 = new CustomWebhook(2,"webhook name","http://test.akto.io","","queryParam=test","body",Method.POST,10,"test@akto.io",0,0,0,ActiveStatus.ACTIVE); + CustomWebhooksDao.instance.insertOne(customWebhook); + CustomWebhooksDao.instance.insertOne(customWebhook2); + + WebhookAction webhookAction = new WebhookAction(); + + Map session = new HashMap<>(); + User user = new User(); + user.setLogin("test@akto.io"); + session.put("user",user); + webhookAction.setSession(session); + + String result = webhookAction.fetchCustomWebhooks(); + assertEquals("SUCCESS",result); + List customWebhooks = CustomWebhooksDao.instance.findAll(new BasicDBObject()); + assertEquals(2,customWebhooks.size()); + } + + @Test + public void testFetchLatestWebhookResult(){ + CustomWebhooksResultDao.instance.getMCollection().drop(); + List customWebhookResults = new ArrayList<>(); + customWebhookResults.add (new CustomWebhookResult(1000,"test@akto.io",1,"message",new ArrayList<>())); + customWebhookResults.add (new CustomWebhookResult(1000,"test@akto.io",2,"message",new ArrayList<>())); + customWebhookResults.add (new CustomWebhookResult(100,"test@akto.io",3,"message",new ArrayList<>())); + CustomWebhooksResultDao.instance.insertMany(customWebhookResults); + + WebhookAction webhookAction = new WebhookAction(); + webhookAction.setId(1000); + + Map session = new HashMap<>(); + User user = new User(); + user.setLogin("test@akto.io"); + session.put("user",user); + webhookAction.setSession(session); + + String result = webhookAction.fetchLatestWebhookResult(); + assertEquals("SUCCESS", result); + assertEquals(2,webhookAction.getCustomWebhookResult().getTimestamp()); + } + + @Test + public void testAddCustomWebhook(){ + CustomWebhooksDao.instance.getMCollection().drop(); + CustomWebhook customWebhook = new CustomWebhook(1,"webhook name","http://test.akto.io","","queryParam=test","body",Method.POST,10,"test@akto.io",0,0,0,ActiveStatus.ACTIVE); + CustomWebhooksDao.instance.insertOne(customWebhook); + + WebhookAction webhookAction = new WebhookAction(); + webhookAction.setWebhookName("webhook name"); + webhookAction.setUrl("http://test.akto.io"); + webhookAction.setHeaderString(""); + webhookAction.setQueryParams("queryParam=test"); + webhookAction.setBody("Sample body"); + webhookAction.setMethod(Method.POST); + webhookAction.setFrequencyInSeconds(10); + webhookAction.setActiveStatus(ActiveStatus.ACTIVE); + + Map session = new HashMap<>(); + User user = new User(); + user.setLogin("test@akto.io"); + session.put("user",user); + webhookAction.setSession(session); + + String result = webhookAction.addCustomWebhook(); + assertEquals("SUCCESS",result); + assertEquals(2, webhookAction.getCustomWebhooks().size()); + } + + @Test + public void testUpdateCustomWebhook(){ + CustomWebhooksDao.instance.getMCollection().drop(); + CustomWebhook customWebhook = new CustomWebhook(1,"webhook name","http://test.akto.io","","queryParam=test","body",Method.POST,10,"test@akto.io",0,0,0,ActiveStatus.ACTIVE); + CustomWebhooksDao.instance.insertOne(customWebhook); + + WebhookAction webhookAction = new WebhookAction(); + webhookAction.setId(1); + webhookAction.setWebhookName("new webhook name"); + webhookAction.setUrl("http://test.akto.io"); + webhookAction.setHeaderString(""); + webhookAction.setQueryParams("newqueryParam=test"); + webhookAction.setBody("New Sample body"); + webhookAction.setMethod(Method.POST); + webhookAction.setFrequencyInSeconds(20); + webhookAction.setActiveStatus(ActiveStatus.ACTIVE); + + Map session = new HashMap<>(); + User user = new User(); + user.setLogin("test@akto.io"); + session.put("user",user); + webhookAction.setSession(session); + + String result = webhookAction.updateCustomWebhook(); + assertEquals("SUCCESS", result); + assertEquals("new webhook name",webhookAction.getCustomWebhooks().get(0).getWebhookName()); + } + + @Test + public void testChangeStatus(){ + CustomWebhooksDao.instance.getMCollection().drop(); + CustomWebhook customWebhook = new CustomWebhook(1,"webhook name","http://test.akto.io","","queryParam=test","body",Method.POST,10,"test@akto.io",0,0,0,ActiveStatus.ACTIVE); + CustomWebhooksDao.instance.insertOne(customWebhook); + + WebhookAction webhookAction = new WebhookAction(); + webhookAction.setId(1); + webhookAction.setActiveStatus(ActiveStatus.INACTIVE); + + Map session = new HashMap<>(); + User user = new User(); + user.setLogin("test@akto.io"); + session.put("user",user); + webhookAction.setSession(session); + + String result = webhookAction.changeStatus(); + assertEquals("SUCCESS",result); + } + + @Test + public void testDeleteCustomWebhook(){ + CustomWebhooksDao.instance.getMCollection().drop(); + CustomWebhook customWebhook = new CustomWebhook(1,"webhook name","http://test.akto.io","","queryParam=test","body",Method.POST,10,"test@akto.io",0,0,0,ActiveStatus.ACTIVE); + CustomWebhook customWebhook2 = new CustomWebhook(2,"webhook name","http://test.akto.io","","queryParam=test","body",Method.POST,10,"test@akto.io",0,0,0,ActiveStatus.ACTIVE); + CustomWebhooksDao.instance.insertOne(customWebhook); + CustomWebhooksDao.instance.insertOne(customWebhook2); + + WebhookAction webhookAction = new WebhookAction(); + webhookAction.setId(1); + Map session = new HashMap<>(); + User user = new User(); + user.setLogin("test@akto.io"); + session.put("user",user); + webhookAction.setSession(session); + String result = webhookAction.deleteCustomWebhook(); + assertEquals("SUCCESS",result); + List customWebhooks = CustomWebhooksDao.instance.findAll(new BasicDBObject()); + assertEquals(1,customWebhooks.size()); + } + +} diff --git a/apps/dashboard/src/test/java/com/akto/action/observe/TestInventoryAction.java b/apps/dashboard/src/test/java/com/akto/action/observe/TestInventoryAction.java new file mode 100644 index 0000000000..10a23f4be7 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/observe/TestInventoryAction.java @@ -0,0 +1,66 @@ +package com.akto.action.observe; + +import com.akto.MongoBasedTest; +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dto.ApiCollection; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.types.CappedSet; +import com.mongodb.BasicDBObject; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashSet; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class TestInventoryAction extends MongoBasedTest { + + private SingleTypeInfo buildHostSti(String url, String method, String param, int apiCollection) { + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + url, method, -1, true, param, SingleTypeInfo.GENERIC, apiCollection, false + ); + return new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(),0,0, 0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, 0,0); + } + + @Test + public void testFetchEndpointsBasedOnHostName() { + SingleTypeInfoDao.instance.getMCollection().drop(); + ApiCollectionsDao.instance.getMCollection().drop(); + + + ApiCollection apiCollection = new ApiCollection(0, "petstore-lb",0, new HashSet<>(), "petstore.com", 0); + ApiCollectionsDao.instance.insertOne(apiCollection); + + SingleTypeInfo sti1 = buildHostSti("url1", "GET", "host", 0); + SingleTypeInfo sti2 = buildHostSti("url1", "POST", "host", 0); + SingleTypeInfo sti3 = buildHostSti("url2", "PUT", "host", 0); + SingleTypeInfo sti4 = buildHostSti("url3", "GET", "host", 0); + SingleTypeInfo sti5 = buildHostSti("url4", "GET", "host", 0); + SingleTypeInfo sti6 = buildHostSti("url5", "GET", "random", 0); + SingleTypeInfo sti7 = buildHostSti("url6", "GET", "host", 1); + + SingleTypeInfoDao.instance.insertMany(Arrays.asList(sti1, sti2, sti3, sti4, sti5, sti6, sti7)); + + InventoryAction inventoryAction = new InventoryAction(); + String result = inventoryAction.fetchEndpointsBasedOnHostName(); + assertEquals("ERROR", result); + + inventoryAction.setHostName("idodopeshit.in"); + result = inventoryAction.fetchEndpointsBasedOnHostName(); + assertEquals("ERROR", result); + + inventoryAction.setHostName(apiCollection.getHostName()); + result = inventoryAction.fetchEndpointsBasedOnHostName(); + assertEquals("SUCCESS", result); + + + assertEquals(5, inventoryAction.getEndpoints().size()); + + BasicDBObject basicDBObject = inventoryAction.getEndpoints().get(0); + assertEquals(2, basicDBObject.size()); + assertNotNull(basicDBObject.get("url")); + assertNotNull(basicDBObject.get("method")); + } +} diff --git a/apps/dashboard/src/test/java/com/akto/action/testing/TestRolesActionTest.java b/apps/dashboard/src/test/java/com/akto/action/testing/TestRolesActionTest.java new file mode 100644 index 0000000000..17fc4c7870 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/testing/TestRolesActionTest.java @@ -0,0 +1,75 @@ +package com.akto.action.testing; + +import com.akto.MongoBasedTest; +import com.akto.dto.User; +import com.akto.dto.data_types.Conditions; +import com.akto.dto.data_types.Predicate; +import com.akto.dto.testing.TestRoles; +import com.mongodb.BasicDBObject; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class TestRolesActionTest extends MongoBasedTest { + + /* + * + * */ + @Test + public void testRoleCreationFlow () { + TestRolesAction action = new TestRolesAction(); + action.setRoleName("admin"); + + + Map session = new HashMap<>(); + User user = new User(); + user.setLogin("test@akto.io"); + session.put("user",user); + action.setSession(session); + + TestRolesAction.RolesConditionUtils orConditionUtils = new TestRolesAction.RolesConditionUtils(); + orConditionUtils.setOperator(Conditions.Operator.OR); + List list = new ArrayList<>(); + list.add(new BasicDBObject() + .append(Predicate.TYPE, Predicate.Type.CONTAINS.name()) + .append(Predicate.VALUE, "contains")); + orConditionUtils.setPredicates(list); + TestRolesAction.RolesConditionUtils andConditionUtils = new TestRolesAction.RolesConditionUtils(); + andConditionUtils.setOperator(Conditions.Operator.AND); + list.clear(); + list.add(new BasicDBObject() + .append(Predicate.TYPE, Predicate.Type.CONTAINS.name()) + .append(Predicate.VALUE, "contains")); + andConditionUtils.setPredicates(list); + + action.setOrConditions(orConditionUtils); + action.setAndConditions(andConditionUtils); + + if (TestRolesAction.SUCCESS.toUpperCase().equals(action.createTestRole())) { + assertEquals("admin", action.getSelectedRole().getName()); + } else { + fail(); + } + + assertEquals(TestRolesAction.SUCCESS.toUpperCase(), action.fetchAllRolesAndLogicalGroups()); + TestRoles role = action.getTestRoles().get(0); + assertEquals("admin", role.getName()); + + list.clear(); + list.add(new BasicDBObject() + .append(Predicate.TYPE, Predicate.Type.CONTAINS.name()) + .append(Predicate.VALUE, "containsAnd")); + andConditionUtils.setPredicates(list); + + assertEquals(TestRolesAction.SUCCESS.toUpperCase(), action.updateTestRoles()); + + + } + +} diff --git a/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java b/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java new file mode 100644 index 0000000000..9019a47c16 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/testing/TestStartTestAction.java @@ -0,0 +1,94 @@ +package com.akto.action.testing; + +import com.akto.MongoBasedTest; +import com.akto.dao.context.Context; +import com.akto.dao.testing.TestingRunDao; +import com.akto.dao.testing.TestingRunResultSummariesDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.testing.*; +import com.akto.dto.testing.TestingRun.State; +import com.akto.dto.type.URLMethods; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class TestStartTestAction extends MongoBasedTest { + + @Test + public void testStopAllTests() { + TestingRunDao.instance.getMCollection().drop(); + + CollectionWiseTestingEndpoints collectionWiseTestingEndpoints = new CollectionWiseTestingEndpoints(1000); + TestingRun testingRun1 = new TestingRun(Context.now(), "", collectionWiseTestingEndpoints,0, TestingRun.State.SCHEDULED, 0, "test"); + + CustomTestingEndpoints customTestingEndpoints = new CustomTestingEndpoints(Collections.singletonList(new ApiInfo.ApiInfoKey(0, "url", URLMethods.Method.GET))); + TestingRun testingRun2 = new TestingRun(Context.now(), "", customTestingEndpoints ,0, TestingRun.State.SCHEDULED, 1, "test"); + + WorkflowTestingEndpoints workflowTestingEndpoints = new WorkflowTestingEndpoints(); + TestingRun testingRun3 = new TestingRun(Context.now(), "", workflowTestingEndpoints,1, TestingRun.State.SCHEDULED, 0, "test"); + + TestingRun testingRun4 = new TestingRun(Context.now(), "", collectionWiseTestingEndpoints,0, TestingRun.State.RUNNING, 0, "test"); + // already completed test + TestingRun testingRun5 = new TestingRun(Context.now(), "", collectionWiseTestingEndpoints,0, TestingRun.State.COMPLETED, 0, "test"); + + TestingRunDao.instance.insertMany(Arrays.asList(testingRun1, testingRun2, testingRun3, testingRun4, testingRun5 )); + + Bson filter = Filters.or( + Filters.eq(TestingRun.STATE, TestingRun.State.SCHEDULED), + Filters.eq(TestingRun.STATE, TestingRun.State.RUNNING) + ); + + List testingRuns = TestingRunDao.instance.findAll(filter); + assertEquals(4, testingRuns.size()); + + StartTestAction startTestAction = new StartTestAction(); + startTestAction.stopAllTests(); + + + testingRuns = TestingRunDao.instance.findAll(filter); + assertEquals(0, testingRuns.size()); + + testingRuns = TestingRunDao.instance.findAll(Filters.eq(TestingRun.STATE, TestingRun.State.COMPLETED)); + assertEquals(1, testingRuns.size()); + assertEquals(testingRun5.getId(), testingRuns.get(0).getId()); + } + + + @Test + public void testFetchTestingRunResultSummaries() { + TestingRunResultSummariesDao.instance.getMCollection().drop(); + + List testingRunResultSummaryList = new ArrayList<>(); + ObjectId testingRunId = new ObjectId(); + TestingRun testingRun = new TestingRun(0, "avneesh@akto.io", new CollectionWiseTestingEndpoints(0), 0, State.COMPLETED, 0, "test"); + testingRun.setId(testingRunId); + for (int startTimestamp=0; startTimestamp < 30; startTimestamp++) { + TestingRunResultSummary testingRunResultSummary = new TestingRunResultSummary( + startTimestamp, startTimestamp+10, new HashMap<>(), 10, testingRunId, testingRunId.toHexString(), 10 + ); + + testingRunResultSummaryList.add(testingRunResultSummary); + } + + TestingRunDao.instance.insertOne(testingRun); + TestingRunResultSummariesDao.instance.insertMany(testingRunResultSummaryList); + + StartTestAction startTestAction = new StartTestAction(); + startTestAction.setTestingRunHexId(testingRunId.toHexString()); + + String result = startTestAction.fetchTestingRunResultSummaries(); + assertEquals("SUCCESS", result); + + List summariesFromDb = startTestAction.getTestingRunResultSummaries(); + assertEquals(startTestAction.limitForTestingRunResultSummary, summariesFromDb.size()); + assertEquals(29, summariesFromDb.get(0).getStartTimestamp()); + assertEquals(10, summariesFromDb.get(summariesFromDb.size()-1).getStartTimestamp()); + + } +} diff --git a/apps/dashboard/src/test/java/com/akto/action/testing_issues/IssuesActionTest.java b/apps/dashboard/src/test/java/com/akto/action/testing_issues/IssuesActionTest.java new file mode 100644 index 0000000000..b06cb26ce2 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/action/testing_issues/IssuesActionTest.java @@ -0,0 +1,68 @@ +package com.akto.action.testing_issues; + +import com.akto.MongoBasedTest; +import com.akto.dao.context.Context; +import com.akto.dao.testing_run_findings.TestingRunIssuesDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.test_run_findings.TestingIssuesId; +import com.akto.dto.test_run_findings.TestingRunIssues; +import com.akto.dto.type.URLMethods; +import com.akto.util.enums.GlobalEnums; +import org.bson.types.ObjectId; +import org.junit.Test; + +import java.util.ArrayList; + +import static org.junit.Assert.assertEquals; + +public class IssuesActionTest extends MongoBasedTest { + + @Test + public void fetchAllIssues() { + + TestingRunIssues issue = new TestingRunIssues( + new TestingIssuesId( + new ApiInfo.ApiInfoKey( + 123, + "url", + URLMethods.Method.POST + ), + GlobalEnums.TestErrorSource.AUTOMATED_TESTING, + GlobalEnums.TestSubCategory.ADD_METHOD_IN_PARAMETER + ), + GlobalEnums.Severity.HIGH, + GlobalEnums.TestRunIssueStatus.OPEN, + Context.now(), + Context.now(), + new ObjectId() + ); + + TestingRunIssuesDao.instance.insertOne(issue); + IssuesAction action = new IssuesAction(); + action.fetchAllIssues(); + assertEquals(issue.getId(), action.getIssues().get(0).getId()); + + issue = new TestingRunIssues( + new TestingIssuesId( + new ApiInfo.ApiInfoKey( + 1234, + "url", + URLMethods.Method.POST + ), + GlobalEnums.TestErrorSource.AUTOMATED_TESTING, + GlobalEnums.TestSubCategory.ADD_METHOD_IN_PARAMETER + ), + GlobalEnums.Severity.HIGH, + GlobalEnums.TestRunIssueStatus.OPEN, + Context.now(), + Context.now(), + new ObjectId() + ); + + ArrayList list = new ArrayList<>(); + list.add(issue); + action.setIssues(list); + + assertEquals(issue.getId(), action.getIssues().get(0).getId()); + } +} diff --git a/apps/dashboard/src/test/java/com/akto/listener/TestInitializerListener.java b/apps/dashboard/src/test/java/com/akto/listener/TestInitializerListener.java new file mode 100644 index 0000000000..0d3fe21e32 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/listener/TestInitializerListener.java @@ -0,0 +1,68 @@ +package com.akto.listener; + +import com.akto.MongoBasedTest; +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dao.context.Context; +import com.akto.dto.ApiCollection; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.types.CappedSet; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; + +public class TestInitializerListener extends MongoBasedTest { + + private SingleTypeInfo generateSti(String url, int apiCollectionId) { + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + url, "GET",200, false, "param#key", SingleTypeInfo.EMAIL, apiCollectionId, false + ); + return new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 0,1000,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + } + + @Test + public void testChangesInfo() { + ApiCollection apiCollection1 = new ApiCollection(0, "coll1", Context.now(), new HashSet<>(), "akto.io", 1); + ApiCollection apiCollection2 = new ApiCollection(1, "coll2", Context.now(), new HashSet<>(), "app.akto.io", 2); + ApiCollection apiCollection3 = new ApiCollection(2, "coll3", Context.now(), new HashSet<>(), null, 3); + ApiCollectionsDao.instance.insertMany(Arrays.asList(apiCollection1, apiCollection2, apiCollection3)); + + SingleTypeInfo sti1 = generateSti("/api/books", 0); + SingleTypeInfo sti2 = generateSti("api/books/INTEGER", 0); + SingleTypeInfo sti3 = generateSti("/api/cars", 1); + SingleTypeInfo sti4 = generateSti("/api/toys", 2); + SingleTypeInfo sti5 = generateSti("/api/bus", 9999); + + SingleTypeInfoDao.instance.insertMany(Arrays.asList(sti1, sti2, sti3, sti4, sti5)); + + InitializerListener.ChangesInfo changesInfo = InitializerListener.getChangesInfo(Context.now(), Context.now()); + assertNotNull(changesInfo); + List newEndpointsLast7Days = changesInfo.newEndpointsLast7Days; + Map newSensitiveParams = changesInfo.newSensitiveParams; + + assertEquals(5,newEndpointsLast7Days.size()); + + assertTrue(newEndpointsLast7Days.contains("GET akto.io/api/books")); + assertTrue(newEndpointsLast7Days.contains("GET akto.io/api/books/INTEGER")); + assertTrue(newEndpointsLast7Days.contains("GET app.akto.io/api/cars")); + assertTrue(newEndpointsLast7Days.contains("GET /api/toys")); + assertTrue(newEndpointsLast7Days.contains("GET /api/bus")); + + assertTrue(newSensitiveParams.containsKey("GET akto.io/api/books: EMAIL")); + assertTrue(newSensitiveParams.containsKey("GET akto.io/api/books/INTEGER: EMAIL")); + assertTrue(newSensitiveParams.containsKey("GET app.akto.io/api/cars: EMAIL")); + assertTrue(newSensitiveParams.containsKey("GET /api/toys: EMAIL")); + assertTrue(newSensitiveParams.containsKey("GET /api/bus: EMAIL")); + } + + @Test + public void testReadAndSaveBurpPluginVersion() { + assertTrue(InitializerListener.burpPluginVersion < 0); + InitializerListener initializerListener = new InitializerListener(); + initializerListener.readAndSaveBurpPluginVersion(); + assertTrue(InitializerListener.burpPluginVersion > 0); + } + +} diff --git a/apps/dashboard/src/test/java/com/akto/listener/TestListener.java b/apps/dashboard/src/test/java/com/akto/listener/TestListener.java new file mode 100644 index 0000000000..21f624de93 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/listener/TestListener.java @@ -0,0 +1,53 @@ +package com.akto.listener; + +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.HashMap; +import java.util.List; + +import com.akto.MongoBasedTest; +import com.akto.dao.CustomDataTypeDao; +import com.akto.dao.context.Context; +import com.akto.dao.pii.PIISourceDao; +import com.akto.dto.CustomDataType; +import com.akto.dto.pii.PIISource; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Updates; + +import org.junit.Test; + +public class TestListener extends MongoBasedTest { + + @Test + public void test() { + Context.accountId.set(1_000_000); + PIISourceDao.instance.getMCollection().drop(); + CustomDataTypeDao.instance.getMCollection().drop(); + + String filePath = new File("").getAbsolutePath(); + String fileUrl = filePath.concat("/src/test/resources/pii_source.json"); + PIISource piiSource = new PIISource(fileUrl, 0, 1638571050, 0, new HashMap<>(), true); + piiSource.setId("A"); + + PIISourceDao.instance.insertOne(piiSource); + InitializerListener.executePIISourceFetch(); + List piiSources = PIISourceDao.instance.findAll(new BasicDBObject()); + List customDataTypes = CustomDataTypeDao.instance.findAll(new BasicDBObject()); + assertTrue(customDataTypes.size() == 4); + assertTrue(piiSources.get(0).getMapNameToPIIType().size() == 2); + + + String fileUrl2 = filePath.concat("/src/test/resources/pii_source_2.json"); + PIISourceDao.instance.updateOne("_id", piiSource.getId(), Updates.set("fileUrl", fileUrl2)); + + InitializerListener.executePIISourceFetch(); + + piiSources = PIISourceDao.instance.findAll(new BasicDBObject()); + customDataTypes = CustomDataTypeDao.instance.findAll(new BasicDBObject()); + assertTrue(customDataTypes.size() == 5); + assertTrue(piiSources.get(0).getMapNameToPIIType().size() == 3); + + } + +} diff --git a/apps/dashboard/src/test/java/com/akto/utils/TestRateLimitCache.java b/apps/dashboard/src/test/java/com/akto/utils/TestRateLimitCache.java new file mode 100644 index 0000000000..d83cff4ef3 --- /dev/null +++ b/apps/dashboard/src/test/java/com/akto/utils/TestRateLimitCache.java @@ -0,0 +1,35 @@ +package com.akto.utils; + +import com.akto.dao.context.Context; +import org.junit.Test; + +import java.util.concurrent.ConcurrentHashMap; + +import static org.junit.Assert.assertEquals; + +public class TestRateLimitCache { + + @Test + public void testDeleteOldData() { + RateLimitCache rateLimitCache = new RateLimitCache(); + int now = Context.now(); + ConcurrentHashMap signInCacheMap = rateLimitCache.cacheMap.get(RateLimitCache.CACHE_TYPE.SIGN_IN); + signInCacheMap.put("ip1", new RateLimitCache.IpInfo(null, now)); + signInCacheMap.put("ip2", new RateLimitCache.IpInfo(null, now-RateLimitCache.thresholdForDeletion-10)); + signInCacheMap.put("ip3", new RateLimitCache.IpInfo(null, now-(RateLimitCache.thresholdForDeletion/2))); + + ConcurrentHashMap sendEmailCacheMap = rateLimitCache.cacheMap.get(RateLimitCache.CACHE_TYPE.SEND_EMAIL); + sendEmailCacheMap.put("ip1", new RateLimitCache.IpInfo(null, now)); + sendEmailCacheMap.put("ip2", new RateLimitCache.IpInfo(null, now-RateLimitCache.thresholdForDeletion-10)); + sendEmailCacheMap.put("ip3", new RateLimitCache.IpInfo(null, now-(RateLimitCache.thresholdForDeletion/2))); + sendEmailCacheMap.put("ip4", new RateLimitCache.IpInfo(null, now-10)); + + assertEquals(signInCacheMap.size(),3); + assertEquals(sendEmailCacheMap.size(),4); + + rateLimitCache.deleteOldData(); + + assertEquals(signInCacheMap.size(),2); + assertEquals(sendEmailCacheMap.size(),3); + } +} diff --git a/apps/dashboard/src/test/resources/pii_source.json b/apps/dashboard/src/test/resources/pii_source.json new file mode 100644 index 0000000000..2192ac3a47 --- /dev/null +++ b/apps/dashboard/src/test/resources/pii_source.json @@ -0,0 +1,32 @@ +{ + "types": [ + { + "name": "Github token", + "sensitive": false, + "regexPattern": ".*GITHUB_TOKEN.*", + "onKey": false, + "active" : true + }, + { + "name": "AWS Secret", + "sensitive": true, + "regexPattern": ".*AWS.*SECRET.*", + "onKey": true, + "active" : false + }, + { + "name": "AA AWS Secret", + "sensitive": true, + "regexPattern": ".*AWS.*SECRET.*", + "onKey": true, + "active" : false + }, + { + "name": "BB AWS Secret", + "sensitive": true, + "regexPattern": ".*AWS.*SECRET.*", + "onKey": true, + "active" : true + } + ] +} \ No newline at end of file diff --git a/apps/dashboard/src/test/resources/pii_source_2.json b/apps/dashboard/src/test/resources/pii_source_2.json new file mode 100644 index 0000000000..ba93c5839c --- /dev/null +++ b/apps/dashboard/src/test/resources/pii_source_2.json @@ -0,0 +1,39 @@ +{ + "types": [ + { + "name": "Github token", + "sensitive": true, + "regexPattern": ".*GITHUB.*TOKEN.*", + "onKey": true, + "active" : true + }, + { + "name": "AWS Secret", + "sensitive": true, + "regexPattern": ".*AWS.*SECRET.*", + "onKey": true, + "active" : false + }, + { + "name": "AWS Access", + "sensitive": true, + "regexPattern": ".*AWS.*ACCESS.*", + "onKey": true, + "active" : true + }, + { + "name": "AA AWS Secret", + "sensitive": true, + "regexPattern": ".*AWS.*SECRET.*", + "onKey": true, + "active" : true + }, + { + "name": "BB AWS Secret", + "sensitive": true, + "regexPattern": ".*AWS.*SECRET.*", + "onKey": true, + "active" : false + } + ] +} \ No newline at end of file diff --git a/apps/dashboard/src/test/resources/restapi_81.har b/apps/dashboard/src/test/resources/restapi_81.har new file mode 100644 index 0000000000..4c5324e89e --- /dev/null +++ b/apps/dashboard/src/test/resources/restapi_81.har @@ -0,0 +1 @@ +{"log":{"version":"1.2","creator":{"name":"Akto","version":"3.19.2"},"pages":[],"entries":[{"startedDateTime":"2022-02-04T09:34:35.225+0530","time":46,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"2"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":46,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.227+0530","time":10,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"1"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":10,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.228+0530","time":16,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"1"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":16,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.227+0530","time":36,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"2"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":36,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.257+0530","time":35,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"3"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":35,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.260+0530","time":25,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d1\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"2"}],"headersSize":683,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":25,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.270+0530","time":48,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d1\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"1"}],"headersSize":683,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":48,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.283+0530","time":17,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"3"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":17,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.285+0530","time":13,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d1\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"3"}],"headersSize":683,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":13,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.301+0530","time":43,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d2\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"1"}],"headersSize":683,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":43,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.323+0530","time":34,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"1"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":34,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.335+0530","time":21,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"2"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":21,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.338+0530","time":18,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"2"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":18,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.338+0530","time":15,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d2\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"2"}],"headersSize":683,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":15,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.348+0530","time":12,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"1"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":12,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.349+0530","time":10,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"3"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":10,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.361+0530","time":15,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"3"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":15,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.361+0530","time":4,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"1"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":4,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.362+0530","time":13,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"1"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":13,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.362+0530","time":9,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"2"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":9,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.370+0530","time":9,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d2\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"3"}],"headersSize":683,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":9,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.378+0530","time":9,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"2"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":9,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.383+0530","time":31,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d3\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"1"}],"headersSize":683,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":31,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.383+0530","time":24,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"3"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":24,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.392+0530","time":32,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d3\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"2"}],"headersSize":683,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":32,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.392+0530","time":32,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"3"}],"postData":{"mimeType":"application/json","params":[],"text":"{}"},"headersSize":763,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":32,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.397+0530","time":26,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d3\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"3"}],"headersSize":683,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":26,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.401+0530","time":14,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"1"}],"postData":{"mimeType":"text/plain","params":[],"text":"{}"},"headersSize":757,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":14,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.415+0530","time":11,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"1"}],"postData":{"mimeType":"text/plain","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":757,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":11,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.424+0530","time":10,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"2"}],"postData":{"mimeType":"text/plain","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":757,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":10,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.434+0530","time":56,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"1"}],"postData":{"mimeType":"text/plain","params":[],"text":"{}"},"headersSize":757,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":56,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.434+0530","time":47,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"2"}],"postData":{"mimeType":"text/plain","params":[],"text":"{}"},"headersSize":757,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":47,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.434+0530","time":56,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"1"}],"postData":{"mimeType":"text/plain","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":757,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":56,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.443+0530","time":29,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"3"}],"postData":{"mimeType":"text/plain","params":[],"text":"{}"},"headersSize":757,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":29,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.462+0530","time":29,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"3"}],"postData":{"mimeType":"text/plain","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":757,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":29,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.472+0530","time":18,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"2"}],"postData":{"mimeType":"text/plain","params":[],"text":"{}"},"headersSize":757,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":18,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.490+0530","time":9,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"3"}],"postData":{"mimeType":"text/plain","params":[],"text":"{}"},"headersSize":757,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":9,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.491+0530","time":17,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"2"}],"postData":{"mimeType":"text/plain","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":757,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":17,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.508+0530","time":45,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"1"}],"postData":{"mimeType":"text/plain","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":757,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":45,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.508+0530","time":46,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"2"}],"postData":{"mimeType":"text/plain","params":[],"text":"{}"},"headersSize":757,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":46,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.508+0530","time":46,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"1"}],"postData":{"mimeType":"text/plain","params":[],"text":"{}"},"headersSize":757,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":46,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.508+0530","time":36,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"3"}],"postData":{"mimeType":"text/plain","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":757,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":36,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.526+0530","time":28,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"2"}],"postData":{"mimeType":"text/plain","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":757,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":28,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.536+0530","time":18,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"3"}],"postData":{"mimeType":"text/plain","params":[],"text":"{}"},"headersSize":757,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":18,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.554+0530","time":7,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"3"}],"postData":{"mimeType":"text/plain","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":757,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":7,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.562+0530","time":25,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"2"}],"postData":{"mimeType":"null","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":751,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":25,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.562+0530","time":25,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"3"}],"postData":{"mimeType":"null","params":[],"text":"{}"},"headersSize":751,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":25,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.562+0530","time":24,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"2"}],"postData":{"mimeType":"null","params":[],"text":"{}"},"headersSize":751,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":24,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.563+0530","time":24,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"1"}],"postData":{"mimeType":"null","params":[],"text":"{}"},"headersSize":751,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":24,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.563+0530","time":15,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"1"}],"postData":{"mimeType":"null","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":751,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":15,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.569+0530","time":19,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d1\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"3"}],"postData":{"mimeType":"null","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":751,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":19,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.588+0530","time":6,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"1"}],"postData":{"mimeType":"null","params":[],"text":"{}"},"headersSize":751,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":6,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.595+0530","time":8,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"3"}],"postData":{"mimeType":"null","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":751,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":8,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.595+0530","time":17,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"2"}],"postData":{"mimeType":"null","params":[],"text":"{}"},"headersSize":751,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":17,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.595+0530","time":17,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"2"}],"postData":{"mimeType":"null","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":751,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":17,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.595+0530","time":17,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"1"}],"postData":{"mimeType":"null","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":751,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":17,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.596+0530","time":16,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d2\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"3"}],"postData":{"mimeType":"null","params":[],"text":"{}"},"headersSize":751,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":16,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.613+0530","time":16,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"1"}],"postData":{"mimeType":"null","params":[],"text":"{}"},"headersSize":751,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":16,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.613+0530","time":16,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"1"}],"postData":{"mimeType":"null","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":751,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":16,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.630+0530","time":16,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"3"}],"postData":{"mimeType":"null","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":751,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":16,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.630+0530","time":26,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"4"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"2"}],"postData":{"mimeType":"null","params":[{"name":"abcd","value":""}],"text":"{}"},"headersSize":751,"bodySize":4},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":26,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.631+0530","time":24,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"3"}],"postData":{"mimeType":"null","params":[],"text":"{}"},"headersSize":751,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":24,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.629+0530","time":2,"request":{"method":"POST","url":"http://localhost:8000/api/cars?p\u003d3\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"Content-Length","value":"2"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Origin","value":"chrome-extension://daoelfolbcclnmhbieoemeepbadlcjgm"},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"2"}],"postData":{"mimeType":"null","params":[],"text":"{}"},"headersSize":751,"bodySize":2},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":2,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.655+0530","time":9,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d1\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"3"}],"headersSize":677,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":9,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.655+0530","time":18,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d1\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"1"}],"headersSize":677,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":18,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.655+0530","time":18,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d1\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"2"}],"headersSize":677,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":18,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.673+0530","time":19,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d2\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"1"}],"headersSize":677,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":19,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.674+0530","time":18,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d2\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"2"}],"headersSize":677,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":18,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.674+0530","time":8,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d2\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"3"}],"headersSize":677,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":8,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.692+0530","time":8,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d3\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"1"}],"headersSize":677,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":8,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.700+0530","time":16,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d3\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"2"}],"headersSize":677,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":16,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.700+0530","time":7,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d1\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"3"}],"headersSize":671,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":7,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.700+0530","time":16,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d3\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"3"}],"headersSize":677,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":16,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.716+0530","time":18,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d1\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"1"}],"headersSize":671,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":18,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.716+0530","time":9,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d1\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"1"},{"name":"q","value":"2"}],"headersSize":671,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":9,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.717+0530","time":16,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d2\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"3"}],"headersSize":671,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":16,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.734+0530","time":8,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d2\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"2"}],"headersSize":671,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":8,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.734+0530","time":17,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d3\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"1"}],"headersSize":671,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":17,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.735+0530","time":16,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d2\u0026q\u003d1","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"2"},{"name":"q","value":"1"}],"headersSize":671,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"116"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":142,"bodySize":116,"content":{"size":116,"mimeType":"text","text":"{\"id\":\"1\",\"isbn\":\"3223\",\"title\":\"Book 1\",\"author\":{\"firstname\":\"Avneesh\",\"lastname\":\"Hota\"},\"timestamp\":1643947471}\n"}},"cache":{},"timings":{"send":0,"wait":16,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.751+0530","time":9,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d3\u0026q\u003d3","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"3"}],"headersSize":671,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"0"},{"name":"Connection","value":"close"},{"name":"Content-Type","value":"application/json"}],"redirectURL":"null","headersSize":99,"bodySize":0,"content":{"size":0,"mimeType":"","text":"{}"}},"cache":{},"timings":{"send":0,"wait":9,"receive":0}},{"startedDateTime":"2022-02-04T09:34:35.752+0530","time":17,"request":{"method":"GET","url":"http://localhost:8000/api/books?p\u003d3\u0026q\u003d2","httpVersion":"HTTP/1.1","origin":"","cookies":[],"headers":[{"name":"Host","value":"localhost:8000"},{"name":"sec-ch-ua","value":"\"Chromium\";v\u003d\"97\", \" Not;A Brand\";v\u003d\"99\""},{"name":"accept-language","value":"en-GB,en-US;q\u003d0.9,en;q\u003d0.8"},{"name":"sec-ch-ua-mobile","value":"?0"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"},{"name":"Content-Type","value":"application/json"},{"name":"accept","value":"text/html,application/xhtml+xml,application/xml;q\u003d0.9,image/avif,image/webp,image/apng,/;q\u003d0.8,application/signed-exchange;v\u003db3;q\u003d0.9"},{"name":"cache-control","value":"max-age\u003d0"},{"name":"upgrade-insecure-requests","value":"1"},{"name":"sec-ch-ua-platform","value":"\"macOS\""},{"name":"Sec-Fetch-Site","value":"none"},{"name":"Sec-Fetch-Mode","value":"cors"},{"name":"Sec-Fetch-Dest","value":"empty"},{"name":"Accept-Encoding","value":"gzip, deflate"},{"name":"Connection","value":"close"}],"queryString":[{"name":"p","value":"3"},{"name":"q","value":"2"}],"headersSize":671,"bodySize":0},"response":{"status":201,"statusText":"Created","httpVersion":"HTTP/1.1","cookies":[],"headers":[{"name":"Date","value":"Fri, 04 Feb 2022 04:04:35 GMT"},{"name":"Content-Length","value":"1"},{"name":"Content-Type","value":"application/json"},{"name":"Connection","value":"close"}],"redirectURL":"null","headersSize":140,"bodySize":1,"content":{"size":1,"mimeType":"text","text":"{}"}},"cache":{},"timings":{"send":0,"wait":17,"receive":0}}]}} \ No newline at end of file diff --git a/apps/dashboard/src/test/resources/restapi_swagger.json b/apps/dashboard/src/test/resources/restapi_swagger.json new file mode 100644 index 0000000000..9a556c105f --- /dev/null +++ b/apps/dashboard/src/test/resources/restapi_swagger.json @@ -0,0 +1,155 @@ +{ + "openapi" : "3.0.1", + "info" : { + "title" : "", + "description" : "Akto generated openAPI file", + "version" : "1.0.0" + }, + "paths" : { + "/api/cars" : { + "description" : "description", + "post" : { + "summary" : "POST request for endpoint http://localhost:8000/api/cars", + "operationId" : "http://localhost:8000/api/cars-POST", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "object", + "properties" : { + "p" : { + "type" : "integer", + "format" : "int32" + }, + "q" : { + "type" : "integer", + "format" : "int32" + } + }, + "description" : "Sample description" + } + } + } + }, + "responses" : { + "201" : { + "description" : "description", + "content" : { + "application/json" : { + "schema" : { + "type" : "object", + "properties" : { + "author" : { + "type" : "object", + "properties" : { + "firstname" : { + "type" : "string" + }, + "lastname" : { + "type" : "string" + } + } + }, + "isbn" : { + "type" : "integer", + "format" : "int32" + }, + "id" : { + "type" : "integer", + "format" : "int32" + }, + "title" : { + "type" : "string" + }, + "timestamp" : { + "type" : "integer", + "format" : "int32" + } + }, + "description" : "Sample description" + } + } + } + } + } + }, + "servers" : [ { + "url" : "http://localhost:8000" + } ], + "parameters" : [ ] + }, + "/api/books" : { + "description" : "description", + "get" : { + "summary" : "GET request for endpoint http://localhost:8000/api/books", + "operationId" : "http://localhost:8000/api/books-GET", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "type" : "object", + "properties" : { + "p" : { + "type" : "integer", + "format" : "int32" + }, + "q" : { + "type" : "integer", + "format" : "int32" + } + }, + "description" : "Sample description" + } + } + } + }, + "responses" : { + "201" : { + "description" : "description", + "content" : { + "application/json" : { + "schema" : { + "type" : "object", + "properties" : { + "author" : { + "type" : "object", + "properties" : { + "firstname" : { + "type" : "string" + }, + "lastname" : { + "type" : "string" + } + } + }, + "isbn" : { + "type" : "integer", + "format" : "int32" + }, + "id" : { + "type" : "integer", + "format" : "int32" + }, + "title" : { + "type" : "string" + }, + "timestamp" : { + "type" : "integer", + "format" : "int32" + } + }, + "description" : "Sample description" + } + } + } + } + } + }, + "servers" : [ { + "url" : "http://localhost:8000" + } ], + "parameters" : [ ] + } + } + } + \ No newline at end of file diff --git a/apps/dashboard/web/META-INF/MANIFEST.MF b/apps/dashboard/web/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..58630c02ef --- /dev/null +++ b/apps/dashboard/web/META-INF/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 + diff --git a/apps/dashboard/web/WEB-INF/jetty-web.xml b/apps/dashboard/web/WEB-INF/jetty-web.xml new file mode 100644 index 0000000000..69169a9ebf --- /dev/null +++ b/apps/dashboard/web/WEB-INF/jetty-web.xml @@ -0,0 +1,25 @@ + + + + / + dashboard.war + + + + + GET + POST + + + + + /* + + + + + + + diff --git a/apps/dashboard/web/WEB-INF/web.xml b/apps/dashboard/web/WEB-INF/web.xml new file mode 100644 index 0000000000..fc49c20b22 --- /dev/null +++ b/apps/dashboard/web/WEB-INF/web.xml @@ -0,0 +1,114 @@ + + + dashboard + + + 60 + + true + + + + + /pages/error.jsp + + + + GzipFilter + org.eclipse.jetty.servlets.GzipFilter + + mimeTypes + text/html,text/plain,text/xml,application/xhtml+xml,text/css,application/javascript,image/svg+xml + + + + GzipFilter + /* + + + + HttpMethodFilter + com.akto.filter.HttpMethodFilter + + + HttpMethodFilter + /api/* + + + + SecurityHeadersFilter + com.akto.filter.SecurityHeadersFilter + + + SecurityHeadersFilter + /* + + + + RateLimitFilter + com.akto.filter.RateLimitFilter + + + RateLimitFilter + /auth/login + /api/inviteUsers + + + + + UserDetailsFilter + com.akto.filter.UserDetailsFilter + + + + UserDetailsFilter + /api/* + /dashboard/* + + + + AuthorizationFilter + com.akto.filter.AuthorizationFilter + + + + AuthorizationFilter + /api/* + + + + InfraMetricsFilter + com.akto.filter.InfraMetricsFilter + + + + InfraMetricsFilter + /api/* + + + + struts2 + org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter + + + + struts2 + /* + + + + com.akto.listener.InitializerListener + + + com.akto.listener.RuntimeListener + + + com.akto.listener.KafkaListener + + + com.akto.listener.InfraMetricsListener + + diff --git a/apps/dashboard/web/pages/error.jsp b/apps/dashboard/web/pages/error.jsp new file mode 100644 index 0000000000..8004546895 --- /dev/null +++ b/apps/dashboard/web/pages/error.jsp @@ -0,0 +1,15 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ taglib uri="/struts-tags" prefix="s"%> + + + + + + Error Page + + +

OOPS! Something went wrong

+ + + \ No newline at end of file diff --git a/apps/dashboard/web/pages/login.jsp b/apps/dashboard/web/pages/login.jsp new file mode 100644 index 0000000000..940d3b363e --- /dev/null +++ b/apps/dashboard/web/pages/login.jsp @@ -0,0 +1,99 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> + + <%-- Using Struts2 Tags in JSP --%> + <%@ taglib uri="/struts-tags" prefix="s" %> + + + + + + + Akto + + + + + + + + + + +
+ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/pages/welcome.jsp b/apps/dashboard/web/pages/welcome.jsp new file mode 100644 index 0000000000..c3a625830f --- /dev/null +++ b/apps/dashboard/web/pages/welcome.jsp @@ -0,0 +1,15 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ taglib uri="/struts-tags" prefix="s"%> + + + + + Welcome Page + + +

Welcome

+
+ + + \ No newline at end of file diff --git a/apps/dashboard/web/public/favicon.svg b/apps/dashboard/web/public/favicon.svg new file mode 100644 index 0000000000..2948689259 --- /dev/null +++ b/apps/dashboard/web/public/favicon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/dashboard/web/public/favicon2.svg b/apps/dashboard/web/public/favicon2.svg new file mode 100644 index 0000000000..3eaf571368 --- /dev/null +++ b/apps/dashboard/web/public/favicon2.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/dashboard/web/public/favicon3.svg b/apps/dashboard/web/public/favicon3.svg new file mode 100644 index 0000000000..f9732edc46 --- /dev/null +++ b/apps/dashboard/web/public/favicon3.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/dashboard/web/public/favicon4.svg b/apps/dashboard/web/public/favicon4.svg new file mode 100644 index 0000000000..575bc84d63 --- /dev/null +++ b/apps/dashboard/web/public/favicon4.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/dashboard/web/public/four_sq.svg b/apps/dashboard/web/public/four_sq.svg new file mode 100644 index 0000000000..204e636dff --- /dev/null +++ b/apps/dashboard/web/public/four_sq.svg @@ -0,0 +1,2 @@ + + diff --git a/apps/dashboard/web/public/manifest.json b/apps/dashboard/web/public/manifest.json new file mode 100644 index 0000000000..4f49bbec6a --- /dev/null +++ b/apps/dashboard/web/public/manifest.json @@ -0,0 +1,28 @@ +{ + "short_name": "Akto", + "name": "Akto", + "icons": [ + { + "src": "/public/favicon2.svg", + "sizes": "192x192", + "purpose": "any" + }, + { + "src": "/public/favicon4.svg", + "sizes": "144x144", + "purpose": "any" + }, + { + "src": "/public/favicon3.svg", + "sizes": "512x512", + "purpose": "any" + } + ], + "start_url": "/login", + "background_color": "#FFFFFF", + "display": "standalone", + "scope": "/", + "theme_color": "#6200EA", + "offline_enabled": true, + "gcm_sender_id": "896372355210" +} diff --git a/apps/dashboard/web/src/apps/chrome_plugin/App.vue b/apps/dashboard/web/src/apps/chrome_plugin/App.vue new file mode 100644 index 0000000000..fe409bda73 --- /dev/null +++ b/apps/dashboard/web/src/apps/chrome_plugin/App.vue @@ -0,0 +1,255 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/chrome_plugin/main.js b/apps/dashboard/web/src/apps/chrome_plugin/main.js new file mode 100644 index 0000000000..0f92e42145 --- /dev/null +++ b/apps/dashboard/web/src/apps/chrome_plugin/main.js @@ -0,0 +1,13 @@ +import Vue from 'vue' +import App from './App.vue' +import vuetify from '@/plugins/vuetify' +import router from './router' +import store from './store/module' + +new Vue({ + el: '#app', + vuetify, + router, + store, + render: h => h(App) +}) diff --git a/apps/dashboard/web/src/apps/chrome_plugin/router/index.js b/apps/dashboard/web/src/apps/chrome_plugin/router/index.js new file mode 100644 index 0000000000..198ed38b0f --- /dev/null +++ b/apps/dashboard/web/src/apps/chrome_plugin/router/index.js @@ -0,0 +1,22 @@ +import Vue from 'vue' +import Router from 'vue-router' +const PageIntroduction = () => import( '@/apps/chrome_plugin/views/Introduction') +import store from '@/apps/main/store/module' +Vue.use(Router) + +const router = new Router({ + mode: 'history', + base: '/src/pages/index.html', + routes: [ + { + path: '/index.html', + redirect: '/' + }, + { + path: '/', + component: PageIntroduction + } + ] +}) + +export default router \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/chrome_plugin/store/endpoints.js b/apps/dashboard/web/src/apps/chrome_plugin/store/endpoints.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/dashboard/web/src/apps/chrome_plugin/store/module.js b/apps/dashboard/web/src/apps/chrome_plugin/store/module.js new file mode 100644 index 0000000000..2644ee0661 --- /dev/null +++ b/apps/dashboard/web/src/apps/chrome_plugin/store/module.js @@ -0,0 +1,14 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import inventory from '@/apps/dashboard/views/observe/inventory/store/module' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + namespaced: true, + modules: { + inventory + } +}) + +export default store \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/chrome_plugin/util/catalog.js b/apps/dashboard/web/src/apps/chrome_plugin/util/catalog.js new file mode 100644 index 0000000000..4225d2af0e --- /dev/null +++ b/apps/dashboard/web/src/apps/chrome_plugin/util/catalog.js @@ -0,0 +1,175 @@ + +let SubType = { + NULL: "NULL", + INTEGER: "INTEGER", + FLOAT: "FLOAT", + BOOL: "BOOL", + STRING: "STRING", + OTHER: "OTHER" +} + +function isInt(n){ + return Number(n) === n && n % 1 === 0; +} + +function isFloat(n){ + return Number(n) === n && n % 1 !== 0; +} + +function patterns() { + let ret = {} + ret["EMAIL"] = new RegExp ("^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$"); + ret["URL"] = new RegExp ("^((((https?|ftps?|gopher|telnet|nntp)://)|(mailto:|news:))(%[0-9A-Fa-f]{2}|[-()_.!~*';/?:@&=+$,A-Za-z0-9])+)([).!';/?:,][[:blank:|:blank:]])?$"); + ret["CREDIT_CARD"] = new RegExp ("^((4\\d{3})|(5[1-5]\\d{2})|(6011)|(7\\d{3}))-?\\d{4}-?\\d{4}-?\\d{4}|3[4,7]\\d{13}$"); + ret["SSN"] = new RegExp ("^\\d{3}-\\d{2}-\\d{4}$"); + ret["UUID"] = new RegExp ("^[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}$"); + ret["PAN_CARD"] = new RegExp ("^[A-Z]{5}[0-9]{4}[A-Z]{1}$"); + ret["PHONE_NUMBER_US"] = new RegExp ("^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$"); + ret["PHONE_NUMBER_INDIA"] = /^(?:\s+|)((0|(?:(\+|)91))(?:\s|-)*(?:(?:\d(?:\s|-)*\d{9})|(?:\d{2}(?:\s|-)*\d{8})|(?:\d{3}(?:\s|-)*\d{7})|\d{10}))(?:\s+|)$/ + return ret +} + +function findSubType(o) { + + let patternMap = patterns() + for(var key in patternMap) { + let value = patternMap[key] + let res = value.exec(o+"") + if (res) { + return key + } + } + + if (+o) { + o = +o + } + + if (o === null) { + return SubType.NULL; + } + + if (isInt(o)) { + return SubType.INTEGER + } + + if (isFloat(o)) { + return SubType.FLOAT + } + + if (typeof o == "boolean") { + return SubType.BOOL + } + + if (typeof o === "string") { + return SubType.STRING; + } + + return SubType.OTHER; +} + +function createSingleTypeInfo(subType) { + return { + type: subType, + values: [] + } +} + +function flattenHelper(obj, result, prefix) { + if (!obj || prefix.length > 100) { + return + } + + if (Array.isArray(obj)) { + for(var index in obj) { + flattenHelper(obj[index], result, prefix) + } + } else if (typeof obj === 'object') { + Object.keys(obj).forEach(key => { + flattenHelper(obj[key], result, prefix+"."+key) + }) + } else { + let info = result[prefix] + if (!info) { + info = {} + result[prefix] = info + } + + let subType = findSubType(obj) + + if(!subType) { + subType = SubType.OTHER + } + + let subTypeInfo = info[subType] + + if (!subTypeInfo) { + subTypeInfo = createSingleTypeInfo(subType) + info[subType] = subTypeInfo + } + + subTypeInfo.values.push(obj) + } +} + +function flatten(details, prefix) { + let ret = {} + if (!details) { + return ret + } + flattenHelper(details, ret, prefix) + return ret +} + +function tryJson(str) { + try { + return JSON.parse(str); + } catch (e) { + return null; + } +} + +function getQueryParams(qs) { + qs = qs.split('+').join(' '); + + var params = {}, + tokens, + re = /[?&]?([^=]+)=([^&]*)/g; + + while (tokens = re.exec(qs)) { + params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]); + } + + return params; +} + +export default { + tryParamsOrJson: obj => { + try { + if (obj == null) { + return {} + } + + if (typeof obj=== "object") { + return flatten(obj, "") + } + + if (typeof obj === "string") { + let json = tryJson(obj, "") + + if (json) { + return flatten(json, "") + } else { + return flatten(getQueryParams(obj), "") + } + } + } catch (e) { + return null; + } + + return {} + }, + + toSuperType: subType => { + return (patterns()[subType]) ? "STRING" : subType + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/chrome_plugin/views/Introduction.vue b/apps/dashboard/web/src/apps/chrome_plugin/views/Introduction.vue new file mode 100644 index 0000000000..e544f37e57 --- /dev/null +++ b/apps/dashboard/web/src/apps/chrome_plugin/views/Introduction.vue @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/App.vue b/apps/dashboard/web/src/apps/dashboard/App.vue new file mode 100644 index 0000000000..3503d4233a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/App.vue @@ -0,0 +1,401 @@ + + + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/appbar/api.js b/apps/dashboard/web/src/apps/dashboard/appbar/api.js new file mode 100644 index 0000000000..e66135478e --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/appbar/api.js @@ -0,0 +1,34 @@ +import request from '@/util/request' +import util from '../util' + +export default { + saveToAccount: function (newAccountName, obj) { + return request({ + url: '/api/createNewAccount', + method: 'post', + data: { + newAccountName + } + }) + }, + goToAccount: function (newAccountId) { + return request({ + url: '/api/goToAccount', + method: 'post', + data: { + newAccountId + } + }).then(resp => { + window.location.href = '/dashboard/testing' + }) + }, + logout() { + return request({ + url: '/api/logout', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/layouts/LayoutWithLeftPane.vue b/apps/dashboard/web/src/apps/dashboard/layouts/LayoutWithLeftPane.vue new file mode 100644 index 0000000000..fa0cd99d3b --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/layouts/LayoutWithLeftPane.vue @@ -0,0 +1,66 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/layouts/LayoutWithRightPane.vue b/apps/dashboard/web/src/apps/dashboard/layouts/LayoutWithRightPane.vue new file mode 100644 index 0000000000..99244155b3 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/layouts/LayoutWithRightPane.vue @@ -0,0 +1,102 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/layouts/LayoutWithTabs.vue b/apps/dashboard/web/src/apps/dashboard/layouts/LayoutWithTabs.vue new file mode 100644 index 0000000000..2ed10ec395 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/layouts/LayoutWithTabs.vue @@ -0,0 +1,114 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/layouts/SimpleLayout.vue b/apps/dashboard/web/src/apps/dashboard/layouts/SimpleLayout.vue new file mode 100644 index 0000000000..d06b214452 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/layouts/SimpleLayout.vue @@ -0,0 +1,49 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/nav/api.js b/apps/dashboard/web/src/apps/dashboard/nav/api.js new file mode 100644 index 0000000000..3128921861 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/nav/api.js @@ -0,0 +1,14 @@ +import request from '@/util/request' + + +export default { + createNewTeam: function (name) { + return request({ + url: 'api/createNewTeam', + method: 'post', + data: { + name + } + }) + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/api.js b/apps/dashboard/web/src/apps/dashboard/shared/api.js new file mode 100644 index 0000000000..518a8d5f04 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/api.js @@ -0,0 +1,24 @@ +import request from '@/util/request' + +export default { + toggleFavourite: function (newStatus, id, type) { + return request({ + url: '/api/toggleFavourite', + method: 'post', + data: { + preference: newStatus ? 1 : 0, + parent_id: id, + type + } + }) + }, + sendGoogleAuthCodeToServer (code) { + return request({ + url: 'api/sendGoogleAuthCodeToServer', + method: 'post', + data: { + code: code + } + }) + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/ACard.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/ACard.vue new file mode 100644 index 0000000000..c71e0e10e3 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/ACard.vue @@ -0,0 +1,74 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/ActionsTray.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/ActionsTray.vue new file mode 100644 index 0000000000..77cfc0f95b --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/ActionsTray.vue @@ -0,0 +1,58 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/BannerHorizontal.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/BannerHorizontal.vue new file mode 100644 index 0000000000..77f0466fbc --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/BannerHorizontal.vue @@ -0,0 +1,59 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/BannerVertical.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/BannerVertical.vue new file mode 100644 index 0000000000..dcde667ff7 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/BannerVertical.vue @@ -0,0 +1,82 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/CodeBlock.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/CodeBlock.vue new file mode 100644 index 0000000000..49ff2c884f --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/CodeBlock.vue @@ -0,0 +1,104 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/CountBox.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/CountBox.vue new file mode 100644 index 0000000000..7275afc8bb --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/CountBox.vue @@ -0,0 +1,75 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/CreateAccountDialog.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/CreateAccountDialog.vue new file mode 100644 index 0000000000..8322e514ac --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/CreateAccountDialog.vue @@ -0,0 +1,76 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/DateRange.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/DateRange.vue new file mode 100644 index 0000000000..951660b314 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/DateRange.vue @@ -0,0 +1,130 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/DonutChart.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/DonutChart.vue new file mode 100644 index 0000000000..a8780e4c8f --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/DonutChart.vue @@ -0,0 +1,65 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/Favourite.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/Favourite.vue new file mode 100644 index 0000000000..ef5ab8ee04 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/Favourite.vue @@ -0,0 +1,51 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/FilterColumn.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/FilterColumn.vue new file mode 100644 index 0000000000..0948657de1 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/FilterColumn.vue @@ -0,0 +1,68 @@ + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/FilterList.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/FilterList.vue new file mode 100644 index 0000000000..826c02fd2d --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/FilterList.vue @@ -0,0 +1,199 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/FilterNumber.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/FilterNumber.vue new file mode 100644 index 0000000000..4a2b57ed54 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/FilterNumber.vue @@ -0,0 +1,55 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/FilterSearch.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/FilterSearch.vue new file mode 100644 index 0000000000..55d33fab89 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/FilterSearch.vue @@ -0,0 +1,108 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/FixedColumnsTable.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/FixedColumnsTable.vue new file mode 100644 index 0000000000..94f8dad638 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/FixedColumnsTable.vue @@ -0,0 +1,354 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/GoogleAuth.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/GoogleAuth.vue new file mode 100644 index 0000000000..5a1ea168d5 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/GoogleAuth.vue @@ -0,0 +1,60 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/GroupAvatars.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/GroupAvatars.vue new file mode 100644 index 0000000000..e4e962f4aa --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/GroupAvatars.vue @@ -0,0 +1,42 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/IconMenu.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/IconMenu.vue new file mode 100644 index 0000000000..1d24021220 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/IconMenu.vue @@ -0,0 +1,49 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/JSONViewer.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/JSONViewer.vue new file mode 100644 index 0000000000..9fa014b51a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/JSONViewer.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/LineChart.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/LineChart.vue new file mode 100644 index 0000000000..81c1179cd7 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/LineChart.vue @@ -0,0 +1,152 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/OwnerName.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/OwnerName.vue new file mode 100644 index 0000000000..9d99785f19 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/OwnerName.vue @@ -0,0 +1,58 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/PieChart.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/PieChart.vue new file mode 100644 index 0000000000..9f51d0a32f --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/PieChart.vue @@ -0,0 +1,67 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/SampleData.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/SampleData.vue new file mode 100644 index 0000000000..b6d8e0461a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/SampleData.vue @@ -0,0 +1,189 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/SampleDataList.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/SampleDataList.vue new file mode 100644 index 0000000000..30c807065a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/SampleDataList.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/SampleSingleSide.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/SampleSingleSide.vue new file mode 100644 index 0000000000..8079113f25 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/SampleSingleSide.vue @@ -0,0 +1,243 @@ + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/ScheduleBox.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/ScheduleBox.vue new file mode 100644 index 0000000000..bd9e45940b --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/ScheduleBox.vue @@ -0,0 +1,182 @@ + + + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/SensitiveChipGroup.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/SensitiveChipGroup.vue new file mode 100644 index 0000000000..baf107d148 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/SensitiveChipGroup.vue @@ -0,0 +1,60 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/SensitiveParamsCard.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/SensitiveParamsCard.vue new file mode 100644 index 0000000000..93557116d4 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/SensitiveParamsCard.vue @@ -0,0 +1,61 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/ServerTable.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/ServerTable.vue new file mode 100644 index 0000000000..5acf93bec2 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/ServerTable.vue @@ -0,0 +1,453 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/SimpleMenu.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/SimpleMenu.vue new file mode 100644 index 0000000000..c74fef9d4e --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/SimpleMenu.vue @@ -0,0 +1,80 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/SimpleTable.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/SimpleTable.vue new file mode 100644 index 0000000000..0d81e85e91 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/SimpleTable.vue @@ -0,0 +1,476 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/SimpleTextField.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/SimpleTextField.vue new file mode 100644 index 0000000000..1d21cc3e46 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/SimpleTextField.vue @@ -0,0 +1,65 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/SlackAuth.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/SlackAuth.vue new file mode 100644 index 0000000000..86867edfed --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/SlackAuth.vue @@ -0,0 +1,41 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/Spinner.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/Spinner.vue new file mode 100644 index 0000000000..4b1d6fedc8 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/Spinner.vue @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/TagChipGroup.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/TagChipGroup.vue new file mode 100644 index 0000000000..48588d0c0c --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/TagChipGroup.vue @@ -0,0 +1,57 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/TestResultsTable.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/TestResultsTable.vue new file mode 100644 index 0000000000..58f32adb9c --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/TestResultsTable.vue @@ -0,0 +1,353 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/UploadFile.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/UploadFile.vue new file mode 100644 index 0000000000..39de6f72e0 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/UploadFile.vue @@ -0,0 +1,68 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/charts/StackedChart.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/charts/StackedChart.vue new file mode 100644 index 0000000000..2423cdd7b5 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/charts/StackedChart.vue @@ -0,0 +1,141 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/help/HelpTooltip.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/help/HelpTooltip.vue new file mode 100644 index 0000000000..921aeb9049 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/help/HelpTooltip.vue @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/inputs/NameInput.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/inputs/NameInput.vue new file mode 100644 index 0000000000..90b47ffe01 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/inputs/NameInput.vue @@ -0,0 +1,69 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/inputs/Search.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/inputs/Search.vue new file mode 100644 index 0000000000..e42f3c7f74 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/inputs/Search.vue @@ -0,0 +1,49 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/FormFieldMenu.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/FormFieldMenu.vue new file mode 100644 index 0000000000..01e2533c1e --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/FormFieldMenu.vue @@ -0,0 +1,67 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/IntegrationCenter.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/IntegrationCenter.vue new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/IntegrationLogo.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/IntegrationLogo.vue new file mode 100644 index 0000000000..4dd553efa6 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/IntegrationLogo.vue @@ -0,0 +1,63 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/IntegrationSummary.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/IntegrationSummary.vue new file mode 100644 index 0000000000..0f76930afb --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/integrations/IntegrationSummary.vue @@ -0,0 +1,61 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/jsontree/JSONView.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/jsontree/JSONView.vue new file mode 100644 index 0000000000..b6dc3aeac7 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/jsontree/JSONView.vue @@ -0,0 +1,184 @@ + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/jsontree/JSONViewItem.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/jsontree/JSONViewItem.vue new file mode 100644 index 0000000000..c61c4e91fb --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/jsontree/JSONViewItem.vue @@ -0,0 +1,322 @@ + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/menus/ApiCollectionGroup.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/menus/ApiCollectionGroup.vue new file mode 100644 index 0000000000..f6c633b858 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/menus/ApiCollectionGroup.vue @@ -0,0 +1,78 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/components/menus/TestsLibrary.vue b/apps/dashboard/web/src/apps/dashboard/shared/components/menus/TestsLibrary.vue new file mode 100644 index 0000000000..49405d2287 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/components/menus/TestsLibrary.vue @@ -0,0 +1,110 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/AWS.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/AWS.vue new file mode 100644 index 0000000000..461d7f4ff2 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/AWS.vue @@ -0,0 +1,44 @@ + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/AktoWhite.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/AktoWhite.vue new file mode 100644 index 0000000000..ce8712891a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/AktoWhite.vue @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/BookBookmark.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/BookBookmark.vue new file mode 100644 index 0000000000..b6e9f0bd85 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/BookBookmark.vue @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/Burpsuite.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/Burpsuite.vue new file mode 100644 index 0000000000..96b6556d3d --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/Burpsuite.vue @@ -0,0 +1,9 @@ + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/CurlyBraces.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/CurlyBraces.vue new file mode 100644 index 0000000000..c132a52ad1 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/CurlyBraces.vue @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/CustomWebhooks.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/CustomWebhooks.vue new file mode 100644 index 0000000000..3a080ce4af --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/CustomWebhooks.vue @@ -0,0 +1,24 @@ + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/GCP.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/GCP.vue new file mode 100644 index 0000000000..8db51515f1 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/GCP.vue @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/GithubIcon.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/GithubIcon.vue new file mode 100644 index 0000000000..a03ea8d595 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/GithubIcon.vue @@ -0,0 +1,10 @@ + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/GithubName.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/GithubName.vue new file mode 100644 index 0000000000..044da2957c --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/GithubName.vue @@ -0,0 +1,10 @@ + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/Postman.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/Postman.vue new file mode 100644 index 0000000000..0f54f20dfa --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/Postman.vue @@ -0,0 +1,10 @@ + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/Restapi.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/Restapi.vue new file mode 100644 index 0000000000..3b2b96a90e --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/Restapi.vue @@ -0,0 +1,19 @@ + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/Slack.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/Slack.vue new file mode 100644 index 0000000000..8931f53d77 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/Slack.vue @@ -0,0 +1,10 @@ + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/Swagger.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/Swagger.vue new file mode 100644 index 0000000000..1dd9b9ef8e --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/Swagger.vue @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/TrashDoubleTick.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/TrashDoubleTick.vue new file mode 100644 index 0000000000..a52c9068d7 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/TrashDoubleTick.vue @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/shared/icons/TrashSingleTick.vue b/apps/dashboard/web/src/apps/dashboard/shared/icons/TrashSingleTick.vue new file mode 100644 index 0000000000..e80d9f1aed --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/shared/icons/TrashSingleTick.vue @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/util.js b/apps/dashboard/web/src/apps/dashboard/util.js new file mode 100644 index 0000000000..cacaad4da8 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/util.js @@ -0,0 +1,5 @@ +export default { + goToAccount: function (accountId, obj) { + + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/issues/PageIssues.vue b/apps/dashboard/web/src/apps/dashboard/views/issues/PageIssues.vue new file mode 100644 index 0000000000..7b46c51d1f --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/issues/PageIssues.vue @@ -0,0 +1,164 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/issues/api.js b/apps/dashboard/web/src/apps/dashboard/views/issues/api.js new file mode 100644 index 0000000000..e7bf359603 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/issues/api.js @@ -0,0 +1,46 @@ +import request from '@/util/request' + +export default { + fetchIssues(skip, limit, filterStatus, filterCollectionsId, filterSeverity, filterSubCategory, startEpoch) { + return request({ + url: 'api/fetchAllIssues', + method: 'post', + data: {skip, limit, filterStatus, filterCollectionsId, filterSeverity, filterSubCategory, startEpoch} + }) + }, + updateIssueStatus(issueId, statusToBeUpdated, ignoreReason) { + return request({ + url: 'api/updateIssueStatus', + method: 'post', + data: {issueId, statusToBeUpdated, ignoreReason} + }) + }, + bulkUpdateIssueStatus (issueIdArray, statusToBeUpdated, ignoreReason) { + return request({ + url: 'api/bulkUpdateIssueStatus', + method: 'post', + data: {issueIdArray, statusToBeUpdated, ignoreReason} + }) + }, + fetchTestingRunResult (issueId) { + return request({ + url: 'api/fetchTestingRunResult', + method: 'post', + data: {issueId} + }) + }, + fetchAffectedEndpoints (issueId) { + return request({ + url: 'api/fetchAffectedEndpoints', + method: 'post', + data: {issueId} + }) + }, + fetchAllSubCategories () { + return request({ + url: 'api/fetchAllSubCategories', + method: 'post', + data: {} + }) + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/issues/components/IssueBox.vue b/apps/dashboard/web/src/apps/dashboard/views/issues/components/IssueBox.vue new file mode 100644 index 0000000000..1f09556a5a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/issues/components/IssueBox.vue @@ -0,0 +1,279 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/issues/components/IssuesDialog.vue b/apps/dashboard/web/src/apps/dashboard/views/issues/components/IssuesDialog.vue new file mode 100644 index 0000000000..0ecdc3edfc --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/issues/components/IssuesDialog.vue @@ -0,0 +1,80 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/issues/components/IssuesFilters.vue b/apps/dashboard/web/src/apps/dashboard/views/issues/components/IssuesFilters.vue new file mode 100644 index 0000000000..c72ffca53b --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/issues/components/IssuesFilters.vue @@ -0,0 +1,386 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/issues/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/issues/store/module.js new file mode 100644 index 0000000000..ca93a9dbd1 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/issues/store/module.js @@ -0,0 +1,141 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import api from '../api' + + +Vue.use(Vuex) + +const state = { + loading: false, + issues: [], + currentPage: 1, + limit: 20, + totalIssuesCount: 0, + filterStatus : ['OPEN'], + filterCollectionsId : [], + filterSeverity : [], + filterSubCategory1 : [], + startEpoch : 0, + selectedIssueIds : [], + testingRunResult: {}, + subCatogoryMap: {}, + subCategoryFromSourceConfigMap: {}, + testCategoryMap: {} +} + +const issues = { + namespaced: true, + state: state, + mutations: { + EMPTY_STATE(state) { + state.loading = false + state.issues = [] + state.limit = 20 + state.totalIssuesCount = 0 + }, + SAVE_ISSUES(state, { issues, totalIssuesCount }) { + state.issues = issues + state.totalIssuesCount = totalIssuesCount + }, + updateCurrentPage(state, {pageIndex}) { + state.currentPage = pageIndex + }, + updateSelectedIssueIds(state, {selectedIssueIds}) { + state.selectedIssueIds = selectedIssueIds + }, + updateFilters(state, {filterStatus, filterCollectionsId, filterSeverity, filterSubCategory1, startEpoch}) { + if (filterStatus !== undefined) { + state.filterStatus = filterStatus + } + if (filterCollectionsId !== undefined) { + state.filterCollectionsId = filterCollectionsId + } + if (filterSeverity !== undefined) { + state.filterSeverity = filterSeverity + } + if (filterSubCategory1 !== undefined) { + state.filterSubCategory1 = filterSubCategory1 + } + if (startEpoch !== undefined) { + state.startEpoch = startEpoch + } + }, + SAVE_TESTING_RESULT(state, {testingRunResult}) { + state.testingRunResult = testingRunResult + } + }, + actions: { + emptyState({ commit }) { + commit('EMPTY_STATE') + }, + loadTestingResult({commit}, {issueId}) { + return api.fetchTestingRunResult(issueId).then((resp) => { + commit('SAVE_TESTING_RESULT',resp) + }).catch(() => { + }) + }, + loadIssues({ commit }) { + commit('EMPTY_STATE') + state.loading = true + return api.fetchIssues((state.currentPage - 1) * state.limit + , state.limit + , state.filterStatus + , state.filterCollectionsId + , state.filterSeverity + , state.filterSubCategory1 + , state.startEpoch) + .then((resp) => { + commit('SAVE_ISSUES', resp) + state.loading = false + }).catch(() => { + state.loading = false + }) + }, + updateIssueStatus({ commit }, { selectedIssueId, selectedStatus, ignoreReason }) { + state.loading = true + return api.updateIssueStatus(selectedIssueId, selectedStatus, ignoreReason).then((resp) => { + state.loading = false + }).catch(() => { + state.loading = false + }) + }, + bulkUpdateIssueStatus({ commit }, { selectedIssueIds, selectedStatus, ignoreReason }) { + state.loading = true + return api.bulkUpdateIssueStatus(selectedIssueIds, selectedStatus, ignoreReason).then((resp) => { + state.loading = false + }).catch(() => { + state.loading = false + }) + }, + fetchAllSubCategories() { + return api.fetchAllSubCategories().then((resp) => { + debugger + state.subCatogoryMap = {} + resp.subCategories.forEach((x) => { + state.subCatogoryMap[x.name] = x + }) + state.subCategoryFromSourceConfigMap = {} + resp.testSourceConfigs.forEach((x) => { + state.subCategoryFromSourceConfigMap[x.id] = x + }) + state.testCategoryMap = {} + resp.categories.forEach((x) => { + state.testCategoryMap[x.name] = x + }) + }).catch((err) => { + console.log(err); + }) + }, + }, + getters: { + getLoading: (state) => state.loading, + getIssues: (state) => state.issues, + getCurrentPage: (state) => state.currentPage, + getLimit: (state) => state.limit, + getTotalIssuesCount: (state) => state.totalIssuesCount, + getSelectedIssueIds: (state) => state.selectedIssueIds, + getTestingRunResult: (state) => state.testingRunResult + } +} + +export default issues \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/marketplace/PageMarketplace.vue b/apps/dashboard/web/src/apps/dashboard/views/marketplace/PageMarketplace.vue new file mode 100644 index 0000000000..73097a3301 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/marketplace/PageMarketplace.vue @@ -0,0 +1,341 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/marketplace/api.js b/apps/dashboard/web/src/apps/dashboard/views/marketplace/api.js new file mode 100644 index 0000000000..2f8b0b4d15 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/marketplace/api.js @@ -0,0 +1,30 @@ +import request from '@/util/request' + +export default { + fetchAllMarketplaceSubcategories() { + return request({ + url: 'api/fetchAllMarketplaceSubcategories', + method: 'post', + data: {} + }) + }, + fetchTestingSources(defaultCreator, subcategory) { + return request({ + url: 'api/fetchTestingSources', + method: 'post', + data: { + defaultCreator, + subcategory + } + }) + }, + addCustomTest({url, category, subcategory, severity, description}) { + return request({ + url: "api/addCustomTest", + method: "post", + data: { + url, category, subcategory, severity, description + } + }) + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/marketplace/components/MPTestCategory.vue b/apps/dashboard/web/src/apps/dashboard/views/marketplace/components/MPTestCategory.vue new file mode 100644 index 0000000000..ad38336cfa --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/marketplace/components/MPTestCategory.vue @@ -0,0 +1,153 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/marketplace/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/marketplace/store/module.js new file mode 100644 index 0000000000..8f9622a171 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/marketplace/store/module.js @@ -0,0 +1,51 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import api from '../api' + +Vue.use(Vuex) + +const state = { + loading: false, + defaultSubcategories: [], + userSubcategories: [] +} + +const marketplace = { + namespaced: true, + state: state, + mutations: { + EMPTY_STATE(state) { + state.loading = false + state.defaultSubcategories = [] + state.userSubcategories = [] + } + }, + actions: { + emptyState({ commit }) { + commit('EMPTY_STATE') + }, + fetchAllMarketplaceSubcategories({commit}) { + commit('EMPTY_STATE') + return api.fetchAllMarketplaceSubcategories().then((resp) => { + for(let index in resp.testSourceConfigs) { + let tsc = resp.testSourceConfigs[index] + let entry = tsc.category+"/"+tsc.subcategory + if (state.defaultSubcategories.indexOf(entry) == -1) { + state.defaultSubcategories.push(entry) + } + + if( tsc.creator !== "default" && state.userSubcategories.indexOf(entry) == -1) { + state.userSubcategories.push(entry) + } + } + }) + } + }, + getters: { + getLoading: (state) => state.loading, + getDefaultSubcategories: (state) => state.defaultSubcategories, + getUserSubcategories: (state) => state.userSubcategories + } +} + +export default marketplace \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/changes/Changes.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/changes/Changes.vue new file mode 100644 index 0000000000..b882c0797a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/changes/Changes.vue @@ -0,0 +1,544 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/changes/api.js b/apps/dashboard/web/src/apps/dashboard/views/observe/changes/api.js new file mode 100644 index 0000000000..4f18f86ea5 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/changes/api.js @@ -0,0 +1,44 @@ +import request from '@/util/request' + +export default { + bulkMarkSensitive(sensitive, items) { + return request({ + url: '/api/bulkMarkSensitive', + method: 'post', + data: { + items, + sensitive + } + }) + }, + fetchChanges(sortKey, sortOrder, skip, limit, filters, filterOperators, startTimestamp, endTimestamp) { + return request({ + url: '/api/fetchChanges', + method: 'post', + data: { + sortKey, + sortOrder, + limit, + skip, + filters: Object.entries(filters).reduce((z, e) => { + z[e[0]] = [...e[1]] + return z + }, {}), + filterOperators, + startTimestamp, + endTimestamp + } + }).then(resp => { + return resp.response.data + }) + }, + fetchNewParametersTrend(startTimestamp, endTimestamp) { + return request({ + url: '/api/fetchNewParametersTrend', + method: 'post', + data: {startTimestamp, endTimestamp} + }).then(resp => { + return resp.data.endpoints + }) + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/changes/components/BatchOperation.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/changes/components/BatchOperation.vue new file mode 100644 index 0000000000..5138409c35 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/changes/components/BatchOperation.vue @@ -0,0 +1,96 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/changes/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/observe/changes/store/module.js new file mode 100644 index 0000000000..cbe04430b2 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/changes/store/module.js @@ -0,0 +1,97 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import api from '../../inventory/api' +import func from "@/util/func" + + +Vue.use(Vuex) + +let functionCompareParamObj = (x, p) => { + return x.url === p.url && x.method === p.method && x.apiCollectionId === p.apiCollectionId +} + +var state = { + loading: false, + fetchTs: 0, + apiCollection: [], + sensitiveParams: [], + apiInfoList: [], + lastFetched: 0, + data_type_names: [] + +} + +const changes = { + namespaced: true, + state: state, + mutations: { + EMPTY_STATE (state) { + state.loading = false + state.fetchTs = 0 + state.apiCollection = [] + state.sensitiveParams = [] + }, + SAVE_API_COLLECTION (state, info) { + state.apiCollection = info.data.endpoints.map(x => {return {...x._id, startTs: x.startTs}}) + state.apiInfoList = info.data.apiInfoList + }, + SAVE_DATA_TYPE_NAMES(state, resp) { + state.data_type_names = [].concat(resp.allDataTypes) + }, + SAVE_SENSITIVE (state, fields) { + state.sensitiveParams = fields + + fields = fields.reduce((z,e) => { + let key = [e.apiCollectionId + "-" + e.url + "-" + e.method] + z[key] = z[key] || new Set() + z[key].add(e.subType || {"name": "CUSTOM"}) + return z + },{}) + + Object.entries(fields).forEach(p => { + let apiCollectionIndex = state.apiCollection.findIndex(e => { + return (e.apiCollectionId + "-" + e.url + "-" + e.method) === p[0] + }) + + if (apiCollectionIndex > -1) { + state.apiCollection[apiCollectionIndex].sensitive = p[1] + } + }) + state.apiCollection = [...state.apiCollection] + } + }, + actions: { + emptyState({commit}, payload, options) { + commit('EMPTY_STATE', payload, options) + }, + loadRecentEndpoints({commit}, {startTimestamp, endTimestamp}, options) { + commit('EMPTY_STATE') + state.loading = true + state.lastFetched = new Date() / 1000 + return api.loadRecentEndpoints(startTimestamp, endTimestamp).then((resp) => { + commit('SAVE_API_COLLECTION', {data: resp.data}, options) + api.loadSensitiveParameters().then(allSensitiveFields => { + commit('SAVE_SENSITIVE', allSensitiveFields.data.endpoints) + }) + state.loading = false + }).catch(() => { + state.loading = false + }) + }, + fetchDataTypeNames({commit}) { + return api.fetchDataTypeNames().then((resp) => { + commit('SAVE_DATA_TYPE_NAMES', resp) + }) + } + }, + getters: { + getFetchTs: (state) => state.fetchTs, + getLoading: (state) => state.loading, + getAPICollection: (state) => state.apiCollection, + isSensitive: (state) => p => state.sensitiveParams && state.sensitiveParams.findIndex(x => { + return functionCompareParamObj(x, p) + }) > 0 + } +} + +export default changes \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/collections/APICollections.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/collections/APICollections.vue new file mode 100644 index 0000000000..1f6d4d586d --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/collections/APICollections.vue @@ -0,0 +1,280 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/collections/api.js b/apps/dashboard/web/src/apps/dashboard/views/observe/collections/api.js new file mode 100644 index 0000000000..c44e86318a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/collections/api.js @@ -0,0 +1,41 @@ +import request from '@/util/request' + +export default { + getAllCollections () { + return request({ + url: '/api/getAllCollections', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + + createCollection(name) { + return request({ + url: '/api/createCollection', + method: 'post', + data: {collectionName:name} + }).then((resp) => { + return resp + }) + }, + + deleteCollection(apiCollectionId) { + return request({ + url: '/api/deleteCollection', + method: 'post', + data: {apiCollectionId} + }).then((resp) => { + return resp + }) + }, + + deleteMultipleCollections(items) { + return request({ + url: '/api/deleteMultipleCollections', + method: 'post', + data: {apiCollections: items} + }) + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/collections/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/observe/collections/store/module.js new file mode 100644 index 0000000000..33c5a99a72 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/collections/store/module.js @@ -0,0 +1,71 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import api from '../api' + + +Vue.use(Vuex) + +var state = { + loading: false, + fetchTs: 0, + apiCollections:[] +} + +const collections = { + namespaced: true, + state: state, + mutations: { + EMPTY_STATE (state) { + state.loading = false + state.fetchTs = 0 + state.apiCollections = [] + }, + SAVE_API_COLLECTION (state, {apiCollections}) { + state.apiCollections = apiCollections + }, + CREATE_COLLECTION (state, {apiCollections}) { + state.apiCollections.push(apiCollections[0]) + }, + DELETE_COLLECTION (state, apiCollectionId) { + const index = state.apiCollections.findIndex(collection => collection.id === apiCollectionId) + state.apiCollections.splice(index,1) + } + }, + actions: { + emptyState({commit}, payload, options) { + commit('EMPTY_STATE', payload, options) + }, + loadAllApiCollections({commit}, options) { + commit('EMPTY_STATE') + state.loading = true + return api.getAllCollections().then((resp) => { + commit('SAVE_API_COLLECTION', resp, options) + state.loading = false + }).catch(() => { + state.loading = false + }) + }, + createCollection({commit}, {name}, options) { + return api.createCollection(name).then((resp) => { + commit('CREATE_COLLECTION', resp, options) + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: `${name} ` +`added successfully!`, + color: 'green' + }) + }).catch((err) => {}) + }, + deleteCollection({commit}, apiCollection, options) { + return api.deleteCollection(apiCollection.apiCollection.id).then((resp) => { + commit('DELETE_COLLECTION', apiCollection.apiCollection.id, options) + }).catch((err) => {}) + } + }, + getters: { + getFetchTs: (state) => state.fetchTs, + getLoading: (state) => state.loading, + getAPICollections: (state) => state.apiCollections + } +} + +export default collections \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/Inventory.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/Inventory.vue new file mode 100644 index 0000000000..3f84d12655 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/Inventory.vue @@ -0,0 +1,140 @@ + + + + +> diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/Observe.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/Observe.vue new file mode 100644 index 0000000000..a61cc07a8d --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/Observe.vue @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/api.js b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/api.js new file mode 100644 index 0000000000..5f1e12aa5d --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/api.js @@ -0,0 +1,356 @@ +import request from '@/util/request' + +export default { + saveContent(apiSpec) { + return request({ + url: '/api/saveContent', + method: 'post', + data: { + apiSpec: apiSpec.swaggerContent, + filename: apiSpec.filename, + apiCollectionId: apiSpec.apiCollectionId + } + }) + }, + loadContent(apiCollectionId) { + return request({ + url: '/api/loadContent', + method: 'post', + data: { + apiCollectionId: apiCollectionId + } + }) + }, + uploadHarFile(content, apiCollectionId, skipKafka) { + return request({ + url: '/api/uploadHar', + method: 'post', + data: { + content, apiCollectionId, skipKafka + } + }) + }, + uploadTcpFile(content, apiCollectionId, skipKafka) { + return request({ + url: '/api/uploadTcp', + method: 'post', + data: { + tcpContent: content, apiCollectionId, skipKafka + } + }) + }, + downloadOpenApiFile(apiCollectionId) { + return request({ + url: '/api/generateOpenApiFile', + method: 'post', + data: { + apiCollectionId + } + }) + }, + exportToPostman(apiCollectionId) { + return request({ + url: '/api/createPostmanApi', + method: 'post', + data: { + apiCollectionId + } + }) + }, + fetchAPICollection (apiCollectionId) { + return request({ + url: '/api/fetchAPICollection', + method: 'post', + data: { + apiCollectionId: apiCollectionId, + useHost: !!window.useHost + } + }).then((resp) => { + return resp + }) + }, + + fetchAllUrlsAndMethods (apiCollectionId) { + return request({ + url: '/api/fetchAllUrlsAndMethods', + method: 'post', + data: { + apiCollectionId: apiCollectionId + } + }).then((resp) => { + return resp + }) + }, + + addSensitiveField (x) { + return request({ + url: 'api/addSensitiveField', + method: 'post', + data: { + ...x + } + }) + }, + listAllSensitiveFields () { + return request({ + url: 'api/listAllSensitiveFields', + method: 'post', + data: {} + }) + }, + loadRecentEndpoints (startTimestamp, endTimestamp) { + return request({ + url: '/api/loadRecentEndpoints', + method: 'post', + data: {startTimestamp, endTimestamp} + }).then((resp) => { + return resp + }) + }, + loadSensitiveParameters (apiCollectionId, url, method) { + return request({ + url: '/api/loadSensitiveParameters', + method: 'post', + data: { + apiCollectionId, + url, + method + } + }).then((resp) => { + return resp + }) + }, + loadParamsOfEndpoint (apiCollectionId, url, method) { + return request({ + url: '/api/loadParamsOfEndpoint', + method: 'post', + data: { + apiCollectionId, + url, + method + } + }).then((resp) => { + return resp + }) + }, + fetchEndpointTrafficData (url, apiCollectionId, method, startEpoch, endEpoch) { + return request({ + url: '/api/fetchEndpointTrafficData', + method: 'post', + data: { + url, apiCollectionId, method, startEpoch, endEpoch + } + }).then((resp) => { + return resp + }) + }, + fetchSampleData (url, apiCollectionId, method) { + return request({ + url: '/api/fetchSampleData', + method: 'post', + data: { + url, apiCollectionId, method + } + }).then((resp) => { + return resp + }) + }, + + fetchSensitiveSampleData(url, apiCollectionId, method) { + return request({ + url: '/api/fetchSensitiveSampleData', + method: 'post', + data: { + url, apiCollectionId, method + } + }).then((resp) => { + return resp + }) + }, + + fetchApiInfoList(apiCollectionId) { + return request({ + url: '/api/fetchApiInfoList', + method: 'post', + data: { + apiCollectionId + } + }).then((resp) => { + return resp + }) + }, + fetchFilters() { + return request({ + url: '/api/fetchFilters', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + convertSampleDataToCurl(sampleData) { + return request({ + url: '/api/convertSampleDataToCurl', + method: 'post', + data: {sampleData} + }).then((resp) => { + return resp + }) + }, + convertSampleDataToBurpRequest(sampleData) { + return request({ + url: '/api/convertSamleDataToBurpRequest', + method: 'post', + data: {sampleData} + }).then((resp) => { + return resp + }) + }, + + fetchDataTypeNames() { + return request({ + url: '/api/fetchDataTypeNames', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + + fetchWorkflowTests() { + return request({ + url: '/api/fetchWorkflowTests', + method: 'post', + data: {} + }) + }, + + createWorkflowTest(nodes, edges, mapNodeIdToWorkflowNodeDetails, state, apiCollectionId) { + return request({ + url: '/api/createWorkflowTest', + method: 'post', + data: {nodes, edges, mapNodeIdToWorkflowNodeDetails, state, apiCollectionId} + }) + }, + + editWorkflowTest(id, nodes, edges, mapNodeIdToWorkflowNodeDetails) { + return request({ + url: '/api/editWorkflowTest', + method: 'post', + data: {id, nodes, edges, mapNodeIdToWorkflowNodeDetails} + }) + }, + + setWorkflowTestState(id, state) { + return request({ + url: '/api/setWorkflowTestState', + method: 'post', + data: {id, state} + }) + }, + + exportWorkflowTestAsString(id) { + return request({ + url: '/api/exportWorkflowTestAsString', + method: 'post', + data: {id} + }) + }, + + editWorkflowNodeDetails(id, nodeId, workflowNodeDetails) { + let mapNodeIdToWorkflowNodeDetails = {} + mapNodeIdToWorkflowNodeDetails[nodeId] = workflowNodeDetails + return request({ + url: '/api/editWorkflowNodeDetails', + method: 'post', + data: {id, mapNodeIdToWorkflowNodeDetails} + }) + }, + + runWorkflowTest(id) { + return request({ + url: '/api/startTest', + method: 'post', + data: { + "testIdConfig" : 1, + "workflowTestId": id, + "type": "WORKFLOW", + testName: id + } + }) + }, + + scheduleWorkflowTest(id, recurringDaily, startTimestamp) { + return request({ + url: '/api/startTest', + method: 'post', + data: { + "testIdConfig" : 1, + "workflowTestId": id, + "type": "WORKFLOW", + "recurringDaily": recurringDaily, + "startTimestamp": startTimestamp, + testName: id + } + }) + }, + + fetchWorkflowTestingRun(workflowId) { + return request({ + url: '/api/fetchWorkflowTestingRun', + method: 'post', + data: { + "workflowTestId" : workflowId + } + }) + }, + + deleteScheduledWorkflowTests(workflowId) { + return request({ + url: '/api/deleteScheduledWorkflowTests', + method: 'post', + data: { + "workflowTestId" : workflowId + } + }) + }, + + fetchWorkflowResult(id) { + return request({ + url: '/api/fetchWorkflowResult', + method: 'post', + data: { + "workflowTestId": id, + } + }) + }, + + downloadWorkflowAsJson(id) { + return request({ + url: '/api/downloadWorkflowAsJson', + method: 'post', + data: { + "id": id, + } + }) + }, + + uploadWorkflowJson(workflowTestJson, apiCollectionId) { + return request({ + url: '/api/uploadWorkflowJson', + method: 'post', + data: { workflowTestJson, apiCollectionId } + }) + }, + + setFalsePositives (falsePositives) { + return request({ + url: '/api/setFalsePositives', + method: 'post', + data: {falsePositives:falsePositives} + }).then((resp) => { + return resp + }) + } + +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/APIEndpoints.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/APIEndpoints.vue new file mode 100644 index 0000000000..fdd4d21500 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/APIEndpoints.vue @@ -0,0 +1,543 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/APIParameters.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/APIParameters.vue new file mode 100644 index 0000000000..8d07b63e8a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/APIParameters.vue @@ -0,0 +1,348 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/Filters.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/Filters.vue new file mode 100644 index 0000000000..ea2eb5a303 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/Filters.vue @@ -0,0 +1,71 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/TestsSelector.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/TestsSelector.vue new file mode 100644 index 0000000000..1454385de6 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/TestsSelector.vue @@ -0,0 +1,249 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/WorkflowTestBuilder.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/WorkflowTestBuilder.vue new file mode 100644 index 0000000000..0336998f11 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/components/WorkflowTestBuilder.vue @@ -0,0 +1,77 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/store/module.js new file mode 100644 index 0000000000..6270ef9bfa --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/inventory/store/module.js @@ -0,0 +1,236 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import api from '../api' +import func from "@/util/func" + + +Vue.use(Vuex) + +var state = { + parametersLoading: false, + endpointsLoading: false, + fetchTs: 0, + apiCollectionId: 0, + apiCollectionName: '', + apiCollection: [], + sensitiveParams: [], + swaggerContent : null, + apiInfoList: [], + filters: [], + lastFetched: 0, + parameters: [], + url: '', + method: '', + unusedEndpoints: [] +} + +let functionCompareEndpoint = (x, p) => { + return x.url === p.url && x.method === p.method && x.apiCollectionId === p.apiCollectionId +} + +let functionCompareParameter = (x, p) => { + return x.responseCode === p.responseCode && x.isHeader === p.isHeader && x.param === p.param +} + +const inventory = { + namespaced: true, + state: state, + mutations: { + EMPTY_STATE (state) { + state.parametersLoading = false + state.endpointsLoading = false + state.fetchTs = 0 + state.apiCollectionId = 0 + state.apiCollection = [] + state.apiCollectionName = '' + state.swaggerContent = null + state.parameters = [] + state.url = '' + state.method = '' + }, + EMPTY_PARAMS (state) { + state.parametersLoading = false + state.parameters = [] + state.url = '' + state.method = '' + }, + SAVE_API_COLLECTION (state, info) { + state.apiCollectionId = info.apiCollectionId + state.apiCollectionName = info.data.name + state.apiCollection = info.data.endpoints.map(x => {return {...x._id, startTs: x.startTs, changesCount: x.changesCount, shadow: x.shadow ? x.shadow : false}}) + state.apiInfoList = info.data.apiInfoList + state.unusedEndpoints = info.unusedEndpoints + + }, + TOGGLE_SENSITIVE (state, p) { + let sensitiveParamIndex = state.sensitiveParams.findIndex(x => { + return functionCompareParameter(x, p) && functionCompareEndpoint(x, p) + }) + + let savedAsSensitive = sensitiveParamIndex < 0 + if (savedAsSensitive) { + p.savedAsSensitive = p.sensitive + state.sensitiveParams.push(p) + } else { + state.sensitiveParams.splice(sensitiveParamIndex, 1) + } + + let paramIndex = state.parameters.findIndex(x => { + return functionCompareParameter(x, p) + }) + + if (paramIndex > -1) { + state.parameters[paramIndex].sensitive = p.sensitive + state.parameters[paramIndex].savedAsSensitive = p.sensitive + } + + state.parameters = [...state.parameters] + }, + SAVE_SENSITIVE (state, fields) { + state.sensitiveParams = fields + + fields.forEach(p => { + let apiCollectionIndex = state.apiCollection.findIndex(x => { + return functionCompareEndpoint(x, p) + }) + + if (apiCollectionIndex > -1) { + if (!state.apiCollection[apiCollectionIndex].sensitive) { + state.apiCollection[apiCollectionIndex].sensitive = new Set() + } + if (!p.subType) { + p.subType = {"name": "CUSTOM"} + } + state.apiCollection[apiCollectionIndex].sensitive.add(p.subType) + } + }) + state.apiCollection = [...state.apiCollection] + }, + SAVE_PARAMS(state, {method, url, parameters}) { + state.method = method + state.url = url + state.parameters = parameters + } + }, + actions: { + emptyState({commit}, payload, options) { + commit('EMPTY_STATE', payload, options) + }, + loadAPICollection({commit}, {apiCollectionId, shouldLoad}, options) { + state.lastFetched = new Date() / 1000 + commit('EMPTY_STATE') + if (shouldLoad) { + state.endpointsLoading = true + } + return api.fetchAPICollection(apiCollectionId).then((resp) => { + commit('SAVE_API_COLLECTION', {data: resp.data, apiCollectionId: apiCollectionId, unusedEndpoints: resp.unusedEndpoints}, options) + api.loadSensitiveParameters(apiCollectionId).then(allSensitiveFields => { + commit('SAVE_SENSITIVE', allSensitiveFields.data.endpoints) + }) + api.loadContent(apiCollectionId).then(resp => { + if(resp && resp.data && resp.data.content) + state.swaggerContent = JSON.parse(resp.data.content) + }) + api.fetchFilters().then(resp => { + let a = resp.runtimeFilters + a.forEach(x => { + state.filters[x.customFieldName] = x + }) + }) + state.endpointsLoading = false + }).catch(() => { + state.endpointsLoading = false + }) + }, + loadParamsOfEndpoint({commit}, {apiCollectionId, url, method}) { + commit('EMPTY_PARAMS') + state.parametersLoading = true + return api.loadParamsOfEndpoint(apiCollectionId, url, method).then(resp => { + api.loadSensitiveParameters(apiCollectionId, url, method).then(allSensitiveFields => { + allSensitiveFields.data.endpoints.filter(x => x.sensitive).forEach(sensitive => { + let index = resp.data.params.findIndex(x => + x.param === sensitive.param && + x.isHeader === sensitive.isHeader && + x.responseCode === sensitive.responseCode + ) + + if (index > -1 && !sensitive.subType) { + resp.data.params[index].savedAsSensitive = true + if (!resp.data.params[index].subType) { + resp.data.params[index].subType = {"name": "CUSTOM"} + } else { + resp.data.params[index].subType = JSON.parse(JSON.stringify(resp.data.params[index].subType)) + } + } + + }) + commit('SAVE_PARAMS', {parameters: resp.data.params, apiCollectionId, url, method}) + state.parametersLoading = false + }) + }) + + }, + toggleSensitiveParam({commit}, paramInfo) { + return api.addSensitiveField(paramInfo).then(resp => { + commit('TOGGLE_SENSITIVE', paramInfo) + return resp + }) + }, + uploadHarFile({commit,state},{content,filename, skipKafka}) { + return api.uploadHarFile(content,state.apiCollectionId,skipKafka).then(resp => { + return resp + }) + }, + downloadOpenApiFile({commit,state}) { + return api.downloadOpenApiFile(state.apiCollectionId).then(resp => { + return resp + }) + }, + exportToPostman({commit,state}) { + return api.exportToPostman(state.apiCollectionId).then(resp => { + return resp + }) + }, + saveContent({ commit, dispatch, state }, {swaggerContent, filename, apiCollectionId}) { + state.endpointsLoading = true + api.saveContent({swaggerContent, filename, apiCollectionId}).then(resp => { + state.filename = filename + state.swaggerContent = swaggerContent + state.apiCollectionId = apiCollectionId + state.endpointsLoading = false + }).catch(() => { + state.endpointsLoading = false + }) + }, + fetchApiInfoList({commit,dispatch, state}, {apiCollectionId}) { + api.fetchApiInfoList(apiCollectionId).then(resp => { + state.apiInfoList = resp.apiInfoList + }) + }, + fetchFilters({commit, dispatch, state}) { + api.fetchFilters().then(resp => { + let a = resp.runtimeFilters + a.forEach(x => { + state.filters[x.customFieldName] = x + }) + }) + }, + uploadWorkflowJson({commit, dispatch, state}, {content}) { + return api.uploadWorkflowJson(content, state.apiCollectionId); + } + }, + getters: { + getFetchTs: (state) => state.fetchTs, + getAPICollection: (state) => state.apiCollection, + getAPICollectionId: (state) => state.apiCollectionId, + getAPICollectionName: (state) => state.apiCollectionName, + isSensitive: (state) => p => state.sensitiveParams && state.sensitiveParams.findIndex(x => { + return functionCompareParameter(x, p) + }) > 0, + getApiInfoList: (state) => state.apiInfoList, + getFilters: (state) => state.filters, + getUnusedEndpoints: (state) => state.unusedEndpoints, + } +} + +export default inventory \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/misc/ParamState.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/misc/ParamState.vue new file mode 100644 index 0000000000..8d23ffd6c4 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/misc/ParamState.vue @@ -0,0 +1,153 @@ + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/misc/api.js b/apps/dashboard/web/src/apps/dashboard/views/observe/misc/api.js new file mode 100644 index 0000000000..53f38df6ca --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/misc/api.js @@ -0,0 +1,11 @@ +import request from '@/util/request' + +export default { + fetchParamsStatus() { + return request({ + url: '/api/fetchParamsStatus', + method: 'post', + data: {} + }) + }, +} diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/sensitive/SensitiveData.vue b/apps/dashboard/web/src/apps/dashboard/views/observe/sensitive/SensitiveData.vue new file mode 100644 index 0000000000..df2e2a4f33 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/sensitive/SensitiveData.vue @@ -0,0 +1,260 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/observe/sensitive/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/observe/sensitive/store/module.js new file mode 100644 index 0000000000..ea4a8297f8 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/observe/sensitive/store/module.js @@ -0,0 +1,123 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import api from '../../inventory/api' + + +Vue.use(Vuex) + +let functionCompareParamObj = (x, p) => { + return x.param === p.param && x.url === p.url && x.method === p.method && x.isHeader === p.isHeader && x.responseCode === p.responseCode && x.apiCollectionId === p.apiCollectionId +} + +var state = { + loading: false, + fetchTs: 0, + apiCollection: [], + sensitiveParams: [] +} + +const sensitive = { + namespaced: true, + state: state, + mutations: { + EMPTY_STATE (state) { + state.loading = false + state.fetchTs = 0 + state.apiCollection = [] + state.sensitiveParams = [] + }, + SAVE_API_COLLECTION (state, info) { + state.apiCollection = info.data.endpoints.filter(x => { + if (!x.subType) { + x.subType = {"name": "CUSTOM"} + } + return (x.subType.name !== "NULL") + }) + }, + SAVE_SENSITIVE (state, fields) { + state.sensitiveParams = fields + + fields.forEach(p => { + let apiCollectionIndex = state.apiCollection.findIndex(x => { + return functionCompareParamObj(x, p) + }) + + if (apiCollectionIndex > -1) { + state.apiCollection[apiCollectionIndex].savedAsSensitive = true + } + }) + state.apiCollection = [...state.apiCollection] + } + }, + actions: { + emptyState({commit}, payload, options) { + commit('EMPTY_STATE', payload, options) + }, + loadSensitiveParameters({commit},{shouldLoad}, options) { + commit('EMPTY_STATE') + if (shouldLoad) { + state.loading = true + } + return api.loadSensitiveParameters(-1).then((resp) => { + commit('SAVE_API_COLLECTION', {data: resp.data}, options) + listAllSensitiveFields().then(allSensitiveFields => { + commit('SAVE_SENSITIVE', allSensitiveFields.data) + }) + state.loading = false + }).catch(() => { + state.loading = false + }) + }, + ignoreForThisApi({commit},data,options){ + let falsePositives={} + let ignoredKeysInSelectedAPIs = { + [data.apiCollection.originalName]: [{ + "param": data.apiCollection.originalName, + "url": data.apiCollection.endpoint, + "method": data.apiCollection.method, + "isHeader":data.apiCollection.location=="Headers", + "apiCollectionId":data.apiCollection.apiCollectionId, + "responseCode":data.apiCollection.responseCode, + "subTypeString":data.apiCollection.type, + "isUrlParam":false + }] + } + let ignoredKeysInAllAPIs = [] + let ignoreData = { + ignoredKeysInSelectedAPIs : ignoredKeysInSelectedAPIs, + ignoredKeysInAllAPIs : ignoredKeysInAllAPIs + } + falsePositives = { [data.apiCollection.type]: ignoreData } + return api.setFalsePositives(falsePositives).then((resp)=>{ + state.loading = false + }).catch(() => { + state.loading = false + }) + }, + ignoreForAllApis({commit},data,options){ + let falsePositives={} + let ignoredKeysInSelectedAPIs = {} + let ignoredKeysInAllAPIs = [data.apiCollection.originalName] + let ignoreData = { + ignoredKeysInSelectedAPIs : ignoredKeysInSelectedAPIs, + ignoredKeysInAllAPIs : ignoredKeysInAllAPIs + } + falsePositives = { [data.apiCollection.type]: ignoreData } + return api.setFalsePositives(falsePositives).then((resp)=>{ + state.loading = false + }).catch(() => { + state.loading = false + }) + } + }, + getters: { + getFetchTs: (state) => state.fetchTs, + getLoading: (state) => state.loading, + getAPICollection: (state) => state.apiCollection, + isSensitive: (state) => p => state.sensitiveParams && state.sensitiveParams.findIndex(x => { + return functionCompareParamObj(x, p) + }) > 0 + } +} + +export default sensitive \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/PageQuickStart.vue b/apps/dashboard/web/src/apps/dashboard/views/quickstart/PageQuickStart.vue new file mode 100644 index 0000000000..de331a675e --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/PageQuickStart.vue @@ -0,0 +1,69 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/api.js b/apps/dashboard/web/src/apps/dashboard/views/quickstart/api.js new file mode 100644 index 0000000000..b1ae4b25da --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/api.js @@ -0,0 +1,107 @@ +import request from '@/util/request' + +export default{ + addBurpToken() { + return request({ + url: '/api/addBurpToken', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + deleteApiToken(apiTokenId) { + return request({ + url: '/api/deleteApiToken', + method: 'post', + data: {apiTokenId} + }).then((resp) => { + return resp + }) + }, + fetchQuickStartPageState() { + return request({ + url: '/api/fetchQuickStartPageState', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + fetchApiTokens() { + return request({ + url: '/api/fetchApiTokens', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + fetchLBs(){ + return request({ + url: '/api/fetchLoadBalancers', + method: 'post', + data: {} + }) + }, + saveLBs(selectedLBs){ + return request({ + url: 'api/saveLoadBalancers', + method: 'post', + data: {selectedLBs} + }) + }, + fetchStackCreationStatus(){ + return request({ + url: 'api/checkStackCreationProgress', + method: 'post', + data: {} + }) + }, + getPostmanCredentials() { + return request({ + url: '/api/getPostmanCredential', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + fetchPostmanWorkspaces(api_key) { + return request({ + url: '/api/fetchPostmanWorkspaces', + method: 'post', + data: {api_key} + }).then((resp) => { + return resp + }) + }, + importPostmanWorkspace(workspace_id) { + return request({ + url: '/api/importPostmanWorkspace', + method: 'post', + data: {workspace_id} + }).then((resp) => { + return resp + }) + }, + fetchBurpPluginInfo() { + return request({ + url: '/api/fetchBurpPluginInfo', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + downloadBurpPluginJar() { + return request({ + url: '/api/downloadBurpPluginJar', + method: 'post', + data: {}, + responseType: 'blob' + }).then((resp) => { + return resp + }) + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/AwsTrafficMirroring.vue b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/AwsTrafficMirroring.vue new file mode 100644 index 0000000000..6e55c0e65e --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/AwsTrafficMirroring.vue @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/BurpSuiteIntegration.vue b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/BurpSuiteIntegration.vue new file mode 100644 index 0000000000..10f389102a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/BurpSuiteIntegration.vue @@ -0,0 +1,53 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/BurpsuiteSource.vue b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/BurpsuiteSource.vue new file mode 100644 index 0000000000..12716ba66c --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/BurpsuiteSource.vue @@ -0,0 +1,159 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/DataSourcesContent.vue b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/DataSourcesContent.vue new file mode 100644 index 0000000000..2818bccc14 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/DataSourcesContent.vue @@ -0,0 +1,127 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/DetailExpansionPanel.vue b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/DetailExpansionPanel.vue new file mode 100644 index 0000000000..fa4c0cd550 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/DetailExpansionPanel.vue @@ -0,0 +1,73 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/GcpTrafficMirroring.vue b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/GcpTrafficMirroring.vue new file mode 100644 index 0000000000..7602dc0cd0 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/GcpTrafficMirroring.vue @@ -0,0 +1,36 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/LoadBalancers.vue b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/LoadBalancers.vue new file mode 100644 index 0000000000..1b25da10ab --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/LoadBalancers.vue @@ -0,0 +1,505 @@ + + + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/PostmanSource.vue b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/PostmanSource.vue new file mode 100644 index 0000000000..95037fdc30 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/PostmanSource.vue @@ -0,0 +1,93 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/SingleDataSource.vue b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/SingleDataSource.vue new file mode 100644 index 0000000000..a4623ec5af --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/quickstart/components/SingleDataSource.vue @@ -0,0 +1,64 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/PageSettings.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/PageSettings.vue new file mode 100644 index 0000000000..3a7ead96a0 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/PageSettings.vue @@ -0,0 +1,169 @@ + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/api.js b/apps/dashboard/web/src/apps/dashboard/views/settings/api.js new file mode 100644 index 0000000000..e7a0c25ea8 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/api.js @@ -0,0 +1,241 @@ +import request from '@/util/request' + +export default { + inviteUsers(apiSpec) { + return request({ + url: '/api/inviteUsers', + method: 'post', + data: { + inviteeName: apiSpec.inviteeName, + inviteeEmail: apiSpec.inviteeEmail, + websiteHostName: apiSpec.websiteHostName, + + } + }) + }, + getTeamData() { + return request({ + url: '/api/getTeamData', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + removeUser (email) { + return request({ + url: '/api/removeUser', + method: 'post', + data: { + email: email + } + }).then((resp) => { + return resp + }) + }, + health() { + return request({ + url: '/api/health', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + fetchPostmanWorkspaces(api_key) { + return request({ + url: '/api/fetchPostmanWorkspaces', + method: 'post', + data: {api_key} + }).then((resp) => { + return resp + }) + }, + addOrUpdatePostmanCred(api_key, workspace_id) { + return request({ + url: '/api/addOrUpdatePostmanCred', + method: 'post', + data: {api_key,workspace_id} + }).then((resp) => { + return resp + }) + }, + getPostmanCredentials() { + return request({ + url: '/api/getPostmanCredential', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + addBurpToken() { + return request({ + url: '/api/addBurpToken', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + addExternalApiToken() { + return request({ + url: '/api/addExternalApiToken', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + deleteApiToken(apiTokenId) { + return request({ + url: '/api/deleteApiToken', + method: 'post', + data: {apiTokenId} + }).then((resp) => { + return resp + }) + }, + fetchApiTokens() { + return request({ + url: '/api/fetchApiTokens', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + addSlackWebhook(webhookUrl) { + return request({ + url: '/api/addSlackWebhook', + method: 'post', + data: {webhookUrl} + }) + }, + deleteSlackWebhook(apiTokenId) { + return request({ + url: '/api/deleteSlackWebhook', + method: 'post', + data: {apiTokenId} + }) + }, + toggleRedactFeature(redactPayload) { + return request({ + url: '/api/toggleRedactFeature', + method: 'post', + data: { + redactPayload + } + }).then((resp) => { + return resp + }) + }, + + updateMergeAsyncOutside() { + return request({ + url: '/api/updateMergeAsyncOutside', + method: 'post', + data: { + + } + }); + }, + + updateSetupType(setupType) { + return request({ + url: '/api/updateSetupType', + method: 'post', + data: { + setupType + } + }).then((resp) => { + return resp + }) + }, + + fetchAdminSettings() { + return request({ + url: '/api/fetchAdminSettings', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + takeUpdate() { + return request({ + url: '/api/takeUpdate', + method: 'post', + data: {} + }) + }, + fetchLogs(logGroupName, startTime, endTime, limit, filterPattern) { + return request({ + url: '/api/fetchLogs', + method: 'post', + data: { + logGroupName, + startTime, + endTime, + limit, + filterPattern + } + }) + }, + addCustomWebhook(webhookName, url, queryParams, method, headerString, body, frequencyInSeconds) { + return request({ + url: '/api/addCustomWebhook', + method: 'post', + data: { + webhookName, url, queryParams, method, headerString, body, frequencyInSeconds + } + }) + }, + updateCustomWebhook(id, webhookName, url, queryParams, method, headerString, body, frequencyInSeconds) { + return request({ + url: '/api/updateCustomWebhook', + method: 'post', + data: { + id, webhookName, url, queryParams, method, headerString, body, frequencyInSeconds + } + }) + }, + fetchCustomWebhooks() { + return request({ + url: '/api/fetchCustomWebhooks', + method: 'post', + data: {} + }) + }, + changeStatus(id, activeStatus) { + return request({ + url: '/api/changeStatus', + method: 'post', + data: {id, activeStatus} + }) + }, + runOnce(id) { + return request({ + url: '/api/runOnce', + method: 'post', + data: {id} + }) + }, + fetchLatestWebhookResult(id) { + return request({ + url: '/api/fetchLatestWebhookResult', + method: 'post', + data: {id} + }) + }, + fetchUserLastLoginTs() { + return request({ + url: '/api/fetchUserLastLoginTs', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/Account.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/Account.vue new file mode 100644 index 0000000000..c08f57ab1a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/Account.vue @@ -0,0 +1,155 @@ + + + + + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/Health.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/Health.vue new file mode 100644 index 0000000000..545170d5eb --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/Health.vue @@ -0,0 +1,135 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/TeamOverview.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/TeamOverview.vue new file mode 100644 index 0000000000..5c9a3e54f3 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/TeamOverview.vue @@ -0,0 +1,344 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/auth_types/AuthTypeDetails.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/auth_types/AuthTypeDetails.vue new file mode 100644 index 0000000000..a3bc50b8b8 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/auth_types/AuthTypeDetails.vue @@ -0,0 +1,213 @@ + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/auth_types/api.js b/apps/dashboard/web/src/apps/dashboard/views/settings/components/auth_types/api.js new file mode 100644 index 0000000000..99fa25cb24 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/auth_types/api.js @@ -0,0 +1,51 @@ + +import request from '@/util/request' + +export default { + fetchCustomAuthTypes() { + return request({ + url: '/api/fetchCustomAuthTypes', + method: 'post', + data: { } + }) + }, + + addCustomAuthType(name, headerKeys, payloadKeys, active) { + return request({ + url: '/api/addCustomAuthType', + method: 'post', + data: { + name, + headerKeys, + payloadKeys, + active + } + + }) + }, + + updateCustomAuthType(name, headerKeys, payloadKeys, active) { + return request({ + url: '/api/updateCustomAuthType', + method: 'post', + data: { + name, + headerKeys, + payloadKeys, + active + } + + }) + }, + + updateCustomAuthTypeStatus(name, active) { + return request({ + url: '/api/updateCustomAuthTypeStatus', + method: 'post', + data: { + name, active + } + }) + }, + +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/auth_types/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/settings/components/auth_types/store/module.js new file mode 100644 index 0000000000..eed5a11639 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/auth_types/store/module.js @@ -0,0 +1,89 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import func from '@/util/func' +import api from '../api' + +Vue.use(Vuex) + +const auth_types = { + namespaced: true, + state: { + auth_types: null, + auth_type: null, + }, + getters: { + getAuthTypes: (state) => state.auth_types, + getAuthType: (state) => state.auth_type + }, + mutations: { + SET_AUTH_TYPES(state, result) { + state.auth_types = result["customAuthTypes"] + state.usersMap = result["usersMap"] + state.auth_types = func.prepareAuthTypes(state.auth_types) + if (state.auth_types && state.auth_types.length > 0) { + state.auth_type = state.auth_types[0] + } + }, + SET_NEW_AUTH_TYPE(state) { + state.auth_type = { + "name": "", + "operator":"OR", + "headerKeyConditions": {"operator": "AND", "predicates": []}, + "payloadKeyConditions": {"operator": "AND", "predicates": []}, + "active": true, + "createNew": true + } + + }, + UPDATE_AUTH_TYPES(state,result) { + let flag = false + state.auth_types.forEach((auth_type,index) => { + if (auth_type["name"] === result["name"]) { + flag = true + state.auth_types[index] = result + state.auth_type = result + } + }) + if (!flag) { + state.auth_types = [result].concat(state.auth_types) + state.auth_type = result + } + + state.auth_types = func.prepareAuthTypes(state.auth_types) + state.auth_types = [].concat(state.auth_types) + } + }, + actions: { + setNewAuthType({commit, dispatch}) { + commit("SET_NEW_AUTH_TYPE") + }, + toggleActivateAuthType({commit, dispatch}, item) { + return api.updateCustomAuthTypeStatus(item.name, !item.active).then((resp) => { + commit("UPDATE_AUTH_TYPES", resp["customAuthType"]); + }) + }, + fetchCustomAuthTypes({commit, dispatch}) { + return api.fetchCustomAuthTypes().then((resp) => { + commit('SET_AUTH_TYPES', resp) + }) + }, + async createAuthType({commit, dispatch, state}, { auth_type, save}) { + let name = auth_type["name"] + let headerKeys = func.generateKeysForApi(auth_type["headerKeyConditions"]["predicates"]) + let payloadKeys = func.generateKeysForApi(auth_type["payloadKeyConditions"]["predicates"]) + let createNew = auth_type["createNew"] ? auth_type["createNew"] : false + let active = auth_type["active"] ? auth_type["active"] : true + if (createNew) { + return api.addCustomAuthType(name, headerKeys, payloadKeys, active).then((resp) => { + commit("UPDATE_AUTH_TYPES", resp["customAuthType"]); + }) + } else { + return api.updateCustomAuthType(name, headerKeys, payloadKeys, active).then((resp)=>{ + commit("UPDATE_AUTH_TYPES", resp["customAuthType"]); + }) + } + }, + } +} + +export default auth_types \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/DataTypes.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/DataTypes.vue new file mode 100644 index 0000000000..7f01ef47a4 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/DataTypes.vue @@ -0,0 +1,83 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/api.js b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/api.js new file mode 100644 index 0000000000..9477d87b81 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/api.js @@ -0,0 +1,67 @@ + +import request from '@/util/request' + +export default { + fetchDataTypes() { + return request({ + url: '/api/fetchDataTypes', + method: 'post', + data: { } + }) + }, + + reviewCustomDataType(id,name,sensitiveAlways,sensitivePosition,operator,keyOperator, keyConditionFromUsers,valueOperator , + valueConditionFromUsers,active, pageNum) { + return request({ + url: '/api/reviewCustomDataType', + method: 'post', + data: { + id, + name, + sensitiveAlways, + sensitivePosition, + operator, + keyOperator, + keyConditionFromUsers, + valueOperator, + valueConditionFromUsers, + active, + pageNum + } + + }) + }, + + saveCustomDataType(id,name,sensitiveAlways,sensitivePosition,operator,keyOperator, keyConditionFromUsers,valueOperator ,valueConditionFromUsers, createNew,active) { + return request({ + url: '/api/saveCustomDataType', + method: 'post', + data: { + id,name,sensitiveAlways,sensitivePosition,operator,keyOperator, keyConditionFromUsers,valueOperator ,valueConditionFromUsers, createNew, active + } + + }) + }, + + saveAktoDataType(name,sensitiveAlways,sensitivePosition) { + return request({ + url:'/api/saveAktoDataType', + method:'post', + data:{ + name,sensitiveAlways,sensitivePosition + } + }) + }, + + toggleActiveParam(name, active) { + return request({ + url: '/api/toggleDataTypeActiveParam', + method: 'post', + data: { + name, active + } + + }) + }, + +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/ConditionsTable.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/ConditionsTable.vue new file mode 100644 index 0000000000..339885062d --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/ConditionsTable.vue @@ -0,0 +1,153 @@ + + + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/DataTypeCard.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/DataTypeCard.vue new file mode 100644 index 0000000000..7b340d0bef --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/DataTypeCard.vue @@ -0,0 +1,90 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/DataTypeDetails.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/DataTypeDetails.vue new file mode 100644 index 0000000000..5c2158543d --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/DataTypeDetails.vue @@ -0,0 +1,371 @@ + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/OperatorComponent.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/OperatorComponent.vue new file mode 100644 index 0000000000..f4283ff29a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/OperatorComponent.vue @@ -0,0 +1,82 @@ + + + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/ReviewTable.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/ReviewTable.vue new file mode 100644 index 0000000000..52f26ec794 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/ReviewTable.vue @@ -0,0 +1,59 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/SimpleConditionComponent.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/SimpleConditionComponent.vue new file mode 100644 index 0000000000..dba584dbb4 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/components/SimpleConditionComponent.vue @@ -0,0 +1,194 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/store/module.js new file mode 100644 index 0000000000..3986f8f30e --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/data_types/store/module.js @@ -0,0 +1,141 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import func from '@/util/func' +import api from '../api' + + +Vue.use(Vuex) + +const data_types = { + namespaced: true, + state: { + data_types: null, + usersMap: {}, + data_type: null, + reviewData: null, + current_sample_data_count: 0, + total_sample_data_count: 100, + }, + getters: { + getDataTypes: (state) => state.data_types, + getDataType: (state) => state.data_type, + getReviewData: (state) => state.reviewData, + getCurrentSampleDataCount: (state) => state.current_sample_data_count, + getTotalSampleDataCount: (state) => state.total_sample_data_count + }, + mutations: { + SET_DATA_TYPES(state, result) { + state.usersMap= result["dataTypes"]["usersMap"] + state.data_types = result["dataTypes"]["customDataTypes"] + // not needed for akto types + state.data_types = state.data_types.concat(result["dataTypes"]["aktoDataTypes"]) + func.prepareDataTypes(state.data_types) + + if (state.data_types && state.data_types.length > 0) { + state.data_type = state.data_types[0] + } + }, + SET_NEW_DATA_TYPE(state) { + state.data_type = { + "name": "", + "operator": "AND", + "keyConditions": {"operator": "AND", "predicates": []}, + "sensitiveAlways": true, + "valueConditions": {"operator": "AND", "predicates": []}, + "active": true, + "sensitivePosition": [], + "createNew": true + } + + }, + SET_REVIEW_DATA(state, result){ + if (!state.reviewData) { + state.reviewData = [] + } + state.reviewData = state.reviewData.concat(result["customSubTypeMatches"]) + state.total_sample_data_count = result["totalSampleDataCount"] + state.current_sample_data_count += result["currentProcessed"] + }, + RESET_REVIEW_DATA(state){ + state.reviewData = [] + state.total_sample_data_count = 100 + state.current_sample_data_count= 0 + }, + UPDATE_DATA_TYPES(state,result) { + let flag = false + state.data_types.forEach((data_type,index) => { + if (data_type["name"] === result["name"]) { + flag = true + state.data_types[index] = result + state.data_type = result + } + }) + + if (!flag) { + state.data_types = [result].concat(state.data_types) + state.data_type = result + } + + func.prepareDataTypes(state.data_types) + state.data_types = [].concat(state.data_types) + + } + }, + actions: { + setNewDataType({commit, dispatch}) { + commit("SET_NEW_DATA_TYPE") + }, + toggleActiveParam({commit, dispatch}, item) { + return api.toggleActiveParam(item.name, !item.active).then((resp) => { + commit("UPDATE_DATA_TYPES", resp["customDataType"]); + }) + }, + fetchDataTypes({commit, dispatch}) { + return api.fetchDataTypes().then((resp) => { + commit('SET_DATA_TYPES', resp) + }) + }, + async createCustomDataType({commit, dispatch, state}, { data_type, save}) { + let id = data_type["id"] + let name = data_type["name"] + let sensitiveAlways = data_type["sensitiveAlways"] + let sensitivePosition = data_type["sensitivePosition"] + let operator = data_type["operator"] + let keyOperator = data_type["keyConditions"]["operator"] + let keyConditionFromUsers = func.preparePredicatesForApi(data_type["keyConditions"]["predicates"]) + let valueOperator = data_type["valueConditions"]["operator"] + let valueConditionFromUsers = func.preparePredicatesForApi(data_type["valueConditions"]["predicates"]) + let createNew = data_type["createNew"] ? data_type["createNew"] : false + let active = data_type["active"] + if (save) { + return api.saveCustomDataType(id,name,sensitiveAlways,sensitivePosition,operator,keyOperator, keyConditionFromUsers,valueOperator ,valueConditionFromUsers, createNew, active).then((resp) => { + commit("UPDATE_DATA_TYPES", resp["customDataType"]); + }) + } else { + commit("RESET_REVIEW_DATA") + let idx = 0; + let batchSize = 1000 + while (idx < 20 && batchSize >= 1000) { + if (state.reviewData && state.reviewData.length > 1000) { + break + } + idx += 1 + let resp = await api.reviewCustomDataType(id,name,sensitiveAlways,sensitivePosition,operator,keyOperator, keyConditionFromUsers,valueOperator ,valueConditionFromUsers,active,idx) + commit("SET_REVIEW_DATA", resp); + batchSize = resp["currentProcessed"] + } + } + }, + async updateAktoDataType({commit,dispatch,state},{data_type}){ + let name = data_type["name"] + let sensitiveAlways = data_type["sensitiveAlways"] + let sensitivePosition = data_type["sensitivePosition"] + + return api.saveAktoDataType(name,sensitiveAlways,sensitivePosition).then((resp) => { + commit("UPDATE_DATA_TYPES", resp["aktoDataType"]); + }) + } + } +} + +export default data_types \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/AktoApiIntegration.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/AktoApiIntegration.vue new file mode 100644 index 0000000000..34b4214faa --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/AktoApiIntegration.vue @@ -0,0 +1,57 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/ApiToken.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/ApiToken.vue new file mode 100644 index 0000000000..64eac043ba --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/ApiToken.vue @@ -0,0 +1,127 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/BurpSuiteIntegration.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/BurpSuiteIntegration.vue new file mode 100644 index 0000000000..a1e83acb5b --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/BurpSuiteIntegration.vue @@ -0,0 +1,59 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/IntegrationCenter.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/IntegrationCenter.vue new file mode 100644 index 0000000000..8e9786a4ca --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/IntegrationCenter.vue @@ -0,0 +1,73 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/IntegrationSelector.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/IntegrationSelector.vue new file mode 100644 index 0000000000..2342d036dc --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/IntegrationSelector.vue @@ -0,0 +1,193 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/Postman.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/Postman.vue new file mode 100644 index 0000000000..72669f5be1 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/Postman.vue @@ -0,0 +1,139 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/SlackIntegration.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/SlackIntegration.vue new file mode 100644 index 0000000000..a0d2b5ec0b --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/SlackIntegration.vue @@ -0,0 +1,90 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/webhook/WebhookBuilder.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/webhook/WebhookBuilder.vue new file mode 100644 index 0000000000..dbab0b56f8 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/webhook/WebhookBuilder.vue @@ -0,0 +1,175 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/webhook/WebhookIntegration.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/webhook/WebhookIntegration.vue new file mode 100644 index 0000000000..cb2c4cdc83 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/integrations/webhook/WebhookIntegration.vue @@ -0,0 +1,274 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/TagConfigDetails.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/TagConfigDetails.vue new file mode 100644 index 0000000000..83f9ae5af7 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/TagConfigDetails.vue @@ -0,0 +1,220 @@ + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/TagSettings.vue b/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/TagSettings.vue new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/api.js b/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/api.js new file mode 100644 index 0000000000..8615978389 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/api.js @@ -0,0 +1,50 @@ + +import request from '@/util/request' + +export default { + fetchTagConfigs() { + return request({ + url: '/api/fetchTagConfigs', + method: 'post', + data: { } + }) + }, + + reviewTagConfig(id, name, keyOperator, keyConditionFromUsers, active, pageNum) { + return request({ + url: '/api/reviewTagConfig', + method: 'post', + data: { + id, + name, + keyOperator, + keyConditionFromUsers, + active, + pageNum + } + + }) + }, + + saveTagConfig(id,name, keyOperator, keyConditionFromUsers, createNew,active) { + return request({ + url: '/api/saveTagConfig', + method: 'post', + data: { + id,name,keyOperator,keyConditionFromUsers, createNew, active + } + + }) + }, + + toggleActiveTagConfig(name, active) { + return request({ + url: '/api/toggleActiveTagConfig', + method: 'post', + data: { + name, active + } + }) + }, + +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/store/module.js new file mode 100644 index 0000000000..b93017c6a5 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/components/tag_configs/store/module.js @@ -0,0 +1,121 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import func from '@/util/func' +import api from '../api' + + +Vue.use(Vuex) + +const tag_configs = { + namespaced: true, + state: { + tag_configs: null, + usersMap: {}, + tag_config: null, + reviewData: null, + current_sample_data_count: 0, + total_sample_data_count: 100, + }, + getters: { + getTagConfigs: (state) => state.tag_configs, + getTagConfig: (state) => state.tag_config, + getReviewData: (state) => state.reviewData, + getCurrentSampleDataCount: (state) => state.current_sample_data_count, + getTotalSampleDataCount: (state) => state.total_sample_data_count + }, + mutations: { + SET_TAG_CONFIGS(state, result) { + state.usersMap= result["tagConfigs"]["usersMap"] + state.tag_configs = result["tagConfigs"]["tagConfigs"] + func.prepareDataTypes(state.tag_configs) + + if (state.tag_configs && state.tag_configs.length > 0) { + state.tag_config = state.tag_configs[0] + } + }, + SET_NEW_TAG_CONFIG(state) { + state.tag_config = { + "name": "", + "keyConditions": {"operator": "AND", "predicates": []}, + "active": true, + "createNew": true + } + + }, + SET_REVIEW_DATA(state, result){ + if (!state.reviewData) { + state.reviewData = [] + } + state.reviewData = state.reviewData.concat(result["customSubTypeMatches"]) + state.total_sample_data_count = result["totalSampleDataCount"] + state.current_sample_data_count += result["currentProcessed"] + }, + RESET_REVIEW_DATA(state){ + state.reviewData = [] + state.total_sample_data_count = 100 + state.current_sample_data_count= 0 + }, + UPDATE_TAG_CONFIGS(state,result) { + let flag = false + state.tag_configs.forEach((tag_config,index) => { + if (tag_config["name"] === result["name"]) { + flag = true + state.tag_configs[index] = result + state.tag_config = result + } + }) + + if (!flag) { + state.tag_configs = [result].concat(state.tag_configs) + state.tag_config = result + } + + func.prepareDataTypes(state.tag_configs) + state.tag_configs = [].concat(state.tag_configs) + + } + }, + actions: { + setNewTagConfig({commit, dispatch}) { + commit("SET_NEW_TAG_CONFIG") + }, + toggleActiveTagConfig({commit, dispatch}, item) { + return api.toggleActiveTagConfig(item.name, !item.active).then((resp) => { + commit("UPDATE_TAG_CONFIGS", resp["tagConfig"]); + }) + }, + fetchTagConfigs({commit, dispatch}) { + return api.fetchTagConfigs().then((resp) => { + commit('SET_TAG_CONFIGS', resp) + }) + }, + async createTagConfig({commit, dispatch, state}, { tag_config, save}) { + let id = tag_config["id"] + let name = tag_config["name"] + let keyOperator = tag_config["keyConditions"]["operator"] + let keyConditionFromUsers = func.preparePredicatesForApi(tag_config["keyConditions"]["predicates"]) + let createNew = tag_config["createNew"] ? tag_config["createNew"] : false + let active = tag_config["active"] + if (save) { + return api.saveTagConfig(id,name, keyOperator, keyConditionFromUsers, createNew, active).then((resp) => { + commit("UPDATE_TAG_CONFIGS", resp["tagConfig"]); + }) + } else { + commit("RESET_REVIEW_DATA") + let idx = 0; + let batchSize = 1000 + while (idx < 20 && batchSize >= 1000) { + if (state.reviewData && state.reviewData.length > 1000) { + break + } + idx += 1 + let resp = await api.reviewTagConfig(id,name, keyOperator, keyConditionFromUsers,active,idx) + commit("SET_REVIEW_DATA", resp); + batchSize = resp["currentProcessed"] + } + } + }, + } +} + +export default tag_configs \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/settings/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/settings/store/module.js new file mode 100644 index 0000000000..5fd6599c1f --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/settings/store/module.js @@ -0,0 +1,119 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import api from '../api' + + +Vue.use(Vuex) + +const team = { + namespaced: true, + state: { + id: null, + users: {}, + name: '', + fetchTs: 0, + team: {}, + redactPayload: null, + mergeAsyncOutside: null, + dashboardVersion: null, + apiRuntimeVersion: null, + setupType: null, + lastLoginTs: null + }, + getters: { + getId: (state) => state.id, + getUsers: (state) => state.users, + getName: (state) => state.name, + getTeam: (state) => state.team, + getRedactPayload: (state) => state.redactPayload, + getDashboardVersion: (state) => state.dashboardVersion, + getApiRuntimeVersion: (state) => state.apiRuntimeVersion, + getSetupType: (state) => state.setupType, + getLastLoginTs: (state) => state.lastLoginTs + }, + mutations: { + SET_TEAM_DETAILS(state, details) { + state.id = details.id + state.users = details.users + state.name = details.name + state.fetchTs = parseInt(new Date().getTime()/1000) + state.team = details.team + }, + EMPTY_STATE(state) { + state.id = null + state.users = {} + state.name = '' + state.fetchTs = 0 + state.team = {} + }, + SET_ADMIN_SETTINGS(state, resp) { + if (!resp.accountSettings) { + state.redactPayload = false + state.apiRuntimeVersion = "-" + state.dashboardVersion = "-" + state.setupType = "PROD" + state.mergeAsyncOutside = false + } else { + state.redactPayload = resp.accountSettings.redactPayload ? resp.accountSettings.redactPayload : false + state.apiRuntimeVersion = resp.accountSettings.apiRuntimeVersion ? resp.accountSettings.apiRuntimeVersion : "-" + state.dashboardVersion = resp.accountSettings.dashboardVersion ? resp.accountSettings.dashboardVersion : "-" + state.redactPayload = resp.accountSettings.redactPayload ? resp.accountSettings.redactPayload : false + state.setupType = resp.accountSettings.setupType + state.mergeAsyncOutside = resp.accountSettings.mergeAsyncOutside || false + } + }, + SET_USER_INFO(state, resp) { + state.lastLoginTs = resp.lastLoginTs + } + }, + actions: { + getTeamData({commit, dispatch}, id) { + return api.getTeamData().then((resp) => { + commit('SET_TEAM_DETAILS', resp) + }) + }, + fetchAdminSettings({commit, dispatch}) { + api.fetchAdminSettings().then((resp => { + commit('SET_ADMIN_SETTINGS', resp) + })) + }, + fetchUserLastLoginTs({commit, dispatch}) { + api.fetchUserLastLoginTs().then((resp => { + commit('SET_USER_INFO', resp) + })) + }, + toggleRedactFeature({commit, dispatch, state}, v) { + api.toggleRedactFeature(v).then((resp => { + state.redactPayload = v + })) + }, + updateMergeAsyncOutside({commit, dispatch, state}) { + api.updateMergeAsyncOutside().then((resp => { + state.mergeAsyncOutside = true + })) + }, + updateSetupType({commit, dispatch, state}, v) { + api.updateSetupType(v).then((resp => { + state.setupType = v + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Setup type updated successfully", + color: 'green' + }) + })) + }, + removeUser({commit, dispatch}, user) { + return api.removeUser(user.login).then(resp => { + api.getTeamData().then((resp) => { + commit('SET_TEAM_DETAILS', resp) + }) + return resp + }) + }, + emptyState({commit, dispatch}) { + commit('EMPTY_STATE') + } + } +} + +export default team \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/PageTesting.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/PageTesting.vue new file mode 100644 index 0000000000..32c192114f --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/PageTesting.vue @@ -0,0 +1,452 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/api.js b/apps/dashboard/web/src/apps/dashboard/views/testing/api.js new file mode 100644 index 0000000000..f88bad2c15 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/api.js @@ -0,0 +1,188 @@ +import request from '@/util/request' + +export default { + fetchActiveTestingDetails() { + return request({ + url: '/api/retrieveAllCollectionTests', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + fetchPastTestingDetails({startTimestamp, endTimestamp}) { + return request({ + url: '/api/retrieveAllCollectionTests', + method: 'post', + data: { + startTimestamp, + endTimestamp + } + }).then((resp) => { + return resp + }) + }, + + fetchTestingRunResultSummaries(startTimestamp, endTimestamp, testingRunHexId) { + return request({ + url: '/api/fetchTestingRunResultSummaries', + method: 'post', + data: { + startTimestamp, + endTimestamp, + testingRunHexId + } + }) + }, + startTestForCustomEndpoints(apiInfoKeyList, testName) { + return request({ + url: '/api/startTest', + method: 'post', + data: {apiInfoKeyList, type: "CUSTOM", testName} + }).then((resp) => { + return resp + }) + }, + + startTestForCollection(apiCollectionId, testName) { + return request({ + url: '/api/startTest', + method: 'post', + data: {apiCollectionId, type: "COLLECTION_WISE", testName} + }).then((resp) => { + return resp + }) + }, + + scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests) { + return request({ + url: '/api/startTest', + method: 'post', + data: {apiInfoKeyList, type: "CUSTOM", startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests} + }).then((resp) => { + return resp + }) + }, + + scheduleTestForCollection(apiCollectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests) { + return request({ + url: '/api/startTest', + method: 'post', + data: {apiCollectionId, type: "COLLECTION_WISE", startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests} + }).then((resp) => { + return resp + }) + }, + + addAuthMechanism(type, requestData, authParamData) { + return request({ + url: '/api/addAuthMechanism', + method: 'post', + data: {type, requestData, authParamData} + }).then((resp) => { + return resp + }) + }, + + fetchTestingRunResults(testingRunResultSummaryHexId) { + return request({ + url: '/api/fetchTestingRunResults', + method: 'post', + data: { + testingRunResultSummaryHexId + } + }).then((resp) => { + return resp + }) + }, + + stopAllTests() { + return request({ + url: '/api/stopAllTests', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + + fetchTestRunResultDetails(testingRunResultHexId) { + return request({ + url: '/api/fetchTestRunResultDetails', + method: 'post', + data: { + testingRunResultHexId + } + }) + }, + + fetchIssueFromTestRunResultDetails(testingRunResultHexId) { + return request({ + url: '/api/fetchIssueFromTestRunResultDetails', + method: 'post', + data: { + testingRunResultHexId + } + }) + }, + + triggerLoginSteps(type, requestData, authParamData) { + return request({ + url: 'api/triggerLoginSteps', + method: 'post', + data: {type, requestData, authParamData} + }).then((resp) => { + return resp + }) + }, + + triggerSingleStep(type, nodeId, requestData) { + return request({ + url: 'api/triggerSingleLoginFlow', + method: 'post', + data: {type, nodeId, requestData} + }).then((resp) => { + return resp + }) + }, + + fetchAuthMechanismData() { + return request({ + url: '/api/fetchAuthMechanismData', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + + fetchOtpData(url) { + return request({ + url: url, + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + + uploadRecordedLoginFlow(content, tokenFetchCommand) { + return request({ + url: '/api/uploadRecordedFlow', + method: 'post', + data: {content, tokenFetchCommand} + }).then((resp) => { + return resp + }) + }, + + fetchRecordedLoginFlow(nodeId) { + return request({ + url: '/api/fetchRecordedFlowOutput', + method: 'post', + data: {nodeId} + }).then((resp) => { + return resp + }) + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/CreateTestingRun.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/CreateTestingRun.vue new file mode 100644 index 0000000000..8aa1a6159b --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/CreateTestingRun.vue @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestResultDetails.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestResultDetails.vue new file mode 100644 index 0000000000..a92db83c62 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestResultDetails.vue @@ -0,0 +1,49 @@ + + + + + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestResultsDialog.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestResultsDialog.vue new file mode 100644 index 0000000000..c360d4d86f --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestResultsDialog.vue @@ -0,0 +1,305 @@ + + + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestingRunResults.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestingRunResults.vue new file mode 100644 index 0000000000..a0fddd428c --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestingRunResults.vue @@ -0,0 +1,355 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestingRunsTable.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestingRunsTable.vue new file mode 100644 index 0000000000..b9ba59daed --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/TestingRunsTable.vue @@ -0,0 +1,118 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/BlankNode.jsx b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/BlankNode.jsx new file mode 100644 index 0000000000..e312945b12 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/BlankNode.jsx @@ -0,0 +1,137 @@ +import React, { useState, useEffect } from 'react'; + +import { Handle, Position } from 'react-flow-renderer'; +import Box from '@mui/material/Box' +import Autocomplete from '@mui/material/Autocomplete'; +import TextField from '@mui/material/TextField' +import FormControl from '@mui/material/FormControl'; +import InputArgumentsDialog from './InputArgumentsDialog.jsx' +import './start-node.css'; +import useStore from './store' +import { Tooltip } from '@mui/material'; + +const BlankNode = (nodeData) => { + const endpointsList = useStore(state => state.endpointsList) + const nodeEndpointMap = useStore(state => state.nodeEndpointMap) + const addNodeEndpoint = useStore(state => state.addNodeEndpoint) + const fetchSampleDataFunc = useStore(state => state.fetchSampleDataFunc) + + let [endpointDetails, setEndpointDetails] = React.useState(nodeEndpointMap[nodeData.id]) + + const fetchAndUpdateSampleData = async (endpointData) => { + const json = await fetchSampleDataFunc(endpointData.endpoint, endpointData.apiCollectionId, endpointData.method) + + return json && + json.sampleDataList && + json.sampleDataList[0] && + json.sampleDataList[0].samples && + json.sampleDataList[0].samples[0] && JSON.parse(json.sampleDataList[0].samples[0]) || {} + + } + + const onEndpointChange = async (event, endpointData) => { + let s = await fetchAndUpdateSampleData(endpointData) + let updatedSampleData = {orig: JSON.stringify(s)} + let newEndpointData = {...endpointData, updatedSampleData: updatedSampleData} + + addNodeEndpoint(nodeData.id, newEndpointData) + setEndpointDetails(newEndpointData) + } + + + const onEndpointInputChange = (event, newInputValue) => { + let endpointData = nodeEndpointMap[nodeData.id] + if (endpointData) { + if (newInputValue !== (endpointData.method + " " + endpointData.endpoint)) { + addNodeEndpoint(nodeData.id, null) + setEndpointDetails(null) + } + } + + } + + return ( +
+ + + +
+ {nodeData.id} + { + {return option.method ? (option.method + " " + option.endpoint) : "";}} + renderOption={(props, option) => ( + + + {option.method} + {option.endpoint} + + + )} + renderInput={(params) => { + let method = ""; + if (params.inputProps.value && params.inputProps.value.indexOf(" ") > -1) { + let origValue = params.inputProps.value + params.inputProps.value = origValue.split(" ")[1]; + method = origValue.split(" ")[0]; + } + return ( + + + + {method} + + ) + }} + + variant="standard" + /> + ) + }} + + > + } + {endpointDetails && } +
+ + +
+ ) +} + +export default BlankNode \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/EndNode.jsx b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/EndNode.jsx new file mode 100644 index 0000000000..6cf08ffd1a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/EndNode.jsx @@ -0,0 +1,29 @@ +import React from 'react'; + +import { Handle, Position } from 'react-flow-renderer'; + +import './start-node.css'; + +const EndNode = () => { + return ( +
+
End
+ + + + +
+ ) +} + +export default EndNode \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/InputArgumentsDialog.jsx b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/InputArgumentsDialog.jsx new file mode 100644 index 0000000000..9dc2165df1 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/InputArgumentsDialog.jsx @@ -0,0 +1,230 @@ +import * as React from 'react'; +import IconButton from '@mui/material/IconButton'; +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faEdit } from '@fortawesome/free-regular-svg-icons' + +import TemplateStringEditor from './TemplateStringEditor.jsx'; +import useStore from './store' + +import Switch from '@mui/material/Switch'; +import FormControlLabel from '@mui/material/FormControlLabel'; + +import Menu from '@mui/material/Menu'; + +import {faCaretSquareDown} from '@fortawesome/free-regular-svg-icons'; +import FormGroup from '@mui/material/FormGroup'; +import Checkbox from '@mui/material/Checkbox'; + + + +import './start-node.css' +import { Divider } from '@mui/material'; + +const RequestEditor = ({sampleApiCall, updatedSampleData, onChangeApiRequest, testValidatorCode, onChangeTestValidatorCode}) => { + let QUERY_PARAMS = "queryParams" + let REQUEST_HEADERS = "requestHeaders" + let REQUEST_PAYLOAD = "requestPayload" + let REQUEST_URL = "requestUrl" + + + let oldParams = sampleApiCall.path.indexOf("?") > -1 ?sampleApiCall.path.split("?")[1] : ""; + let oldHeaders = sampleApiCall.requestHeaders + let oldPayload = sampleApiCall.requestPayload + let oldUrl = sampleApiCall.path.split("?")[0] + + const onChangeQueryParams = (newParams) => { + onChangeApiRequest(QUERY_PARAMS, newParams); + } + + const onChangeRequestHeaders = (newHeaders) => { + onChangeApiRequest(REQUEST_HEADERS, newHeaders); + } + + const onChangeRequestPayload = (newPayload) => { + onChangeApiRequest(REQUEST_PAYLOAD, newPayload); + } + + const onChangeRequestUrl = (newUrl) => { + onChangeApiRequest(REQUEST_URL, newUrl); + } + + const onChangeTestValidator = (newCondition) => { + onChangeApiRequest(TEST_VALIDATOR, newCondition); + } + + return ( +
+
+
+
[Request] URL
+
+ +
+
[Request] Query params
+
+ +
+
[Request] Headers
+
+ {} +
+
[Request] Payload
+
+ +
+
+
+
[Response] Headers
+
+ {sampleApiCall.responseHeaders} +
+
[Response] Payload
+
+ {sampleApiCall.responsePayload} +
+
+
+ +
+
Test Validator Code
+
+ +
+
+
+
+ ) +} + +export default function InputArgumentsDialog({nodeId, endpointDetails}) { + + const [open, setOpen] = React.useState(false); + + const addNodeEndpoint = useStore(state => state.addNodeEndpoint) + const nodeEndpointMap = useStore(state => state.nodeEndpointMap) + const setApiType = useStore(state => state.setApiType) + const setRedirect = useStore(state => state.setRedirect) + const [sampleData, updateSampleData] = React.useState({}) + const setValidatorCode = useStore(state => state.setValidatorCode) + const setSleep = useStore(state => state.setSleep) + + React.useEffect(() => { + if (!endpointDetails) return; + let updatedSampleData = endpointDetails["updatedSampleData"] + if (!updatedSampleData) return; + updateSampleData(JSON.parse(updatedSampleData["orig"]) || {}) + }, [endpointDetails]) + + const onChangeApiRequest = (key, newData) => { + let currNewSampleData = {orig: JSON.stringify(sampleData)} + currNewSampleData[key] = newData + let updatedSampleData = {...nodeEndpointMap[nodeId].updatedSampleData, ...currNewSampleData} + addNodeEndpoint(nodeId, {...endpointDetails, updatedSampleData}) + } + + const onChangeTestValidatorCode = (val) => { + setValidatorCode(nodeId, val) + } + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + const handleChangePoll = (event) => { + let isPoll = event.target.checked + let type = isPoll ? "POLL" : "API" + setApiType(nodeId, type) + }; + + const checkedPoll = () => { + return endpointDetails["type"] === "POLL" + } + + const handleChangeRedirect = (event) => { + let redirect = event.target.checked + setRedirect(nodeId, !redirect) + }; + + const handleSleep = (event) => { + let waitInSeconds = event.target.checked ? 60 : 0 + setSleep(nodeId, waitInSeconds) + }; + + const checkedRedirect = () => { + return !endpointDetails["overrideRedirect"] + } + + const checkedSleep = () => { + return endpointDetails["waitInSeconds"] > 0 + } + + const [anchorEl, setAnchorEl] = React.useState(null); + const openSettings = Boolean(anchorEl); + const handleSettingsClick = (event) => { + setAnchorEl(event.currentTarget); + }; + const handleSettingsClose = () => { + setAnchorEl(null); + }; + + + return ( +
+
+ + + +
+ +
+ + +
+
+ + + + + + } label="Sleep" /> + } label="Auto Redirect" /> + + +
+ +
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/ScheduleBox.jsx b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/ScheduleBox.jsx new file mode 100644 index 0000000000..1ebccff930 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/ScheduleBox.jsx @@ -0,0 +1,93 @@ + +import React, { useState, useEffect, Component} from 'react' +import { TimePicker } from '@mui/x-date-pickers/TimePicker'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import dayjs from 'dayjs'; +import TextField from '@mui/material/TextField'; +import Grid from '@mui/material/Grid'; +import { Button } from '@mui/material'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import func from '@/util/func' + + + +const ScheduleBox = (props) => { + let testingRun = props.testingRun; + let saveFn = props.saveFn + let deleteFn = props.deleteFn + + const [recurring, setRecurring] = useState(false) + const [startTimestamp, setStartTimestamp] = useState(dayjs()) + + useEffect(() => { + if (testingRun) { + setRecurring(testingRun.periodInSeconds > 0) + setStartTimestamp(dayjs.unix(testingRun.scheduleTimestamp)) + } + }, [testingRun]) + + + const handleRunDailyChange = (event) => { + setRecurring(event.target.checked) + } + + const handleChange = (x) => { + setStartTimestamp(x) + } + + const save = () => { + let hours = startTimestamp.$H + let minutes = startTimestamp.$m + let dayStart = +func.dayStart(+new Date()); + let actualTs = parseInt(dayStart/1000) + hours * 60 * 60 + minutes * 60 + + saveFn(recurring, actualTs) + } + + const deleteSchedule = () => { + deleteFn() + } + + const label = () => { + return testingRun ? "Currently scheduled at" : "Schedule at" + } + + const finalButton = () => { + if (testingRun) { + return + } else { + return + } + } + + return ( + + + + } + disabled={testingRun ? true: false} + /> + + + + } + label="Run daily" + /> + + + {finalButton()} + + + + ) +} + + +export default ScheduleBox \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/StartNode.jsx b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/StartNode.jsx new file mode 100644 index 0000000000..23d8ab1186 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/StartNode.jsx @@ -0,0 +1,29 @@ +import React from 'react'; + +import { Handle, Position } from 'react-flow-renderer'; + +import './start-node.css'; + +const StartNode = () => { + return ( +
+
Start
+ + + + +
+ ) +} + +export default StartNode \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/TemplateStringEditor.jsx b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/TemplateStringEditor.jsx new file mode 100644 index 0000000000..9c4cfbeb29 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/TemplateStringEditor.jsx @@ -0,0 +1,40 @@ +import React, { useState, useEffect } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faEdit, faCheckSquare } from '@fortawesome/free-regular-svg-icons' + +import InputBase from "@mui/material/TextField" +import IconButton from "@mui/material/IconButton" +import TextFieldCloseable from './TextFieldCloseable.jsx' + + +import './start-node.css'; + +const TemplateStringEditor = ({defaultText, onChange, usePureJs=false}) => { + + const [toggle, setToggle] = useState(true); + const toggleChecked = () => { + if (!toggle) { + onChange(text) + } + setToggle(toggle => !toggle); + } + let [text, setText] = React.useState(defaultText); + + const onChangeInputBase = (a, b) => { + setText(a.target.value) + } + + return ( +
+ {toggle && } + {!toggle && } +
+ + + +
+
+ ); + } + +export default TemplateStringEditor \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/TextFieldCloseable.jsx b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/TextFieldCloseable.jsx new file mode 100644 index 0000000000..b148c1321a --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/TextFieldCloseable.jsx @@ -0,0 +1,61 @@ +import React from 'react'; + +import TextField from "@mui/material/TextField" + +import './start-node.css'; + +const TextFieldCloseable = ({text, usePureJs=false}) => { + + var r = /\#\[.*?]#/g; + let m = r.exec(text) + + if (usePureJs) { + return {JavaScriptBlock(text)} + } + + return ( + + {m && {VariableOutside(text.substr(0, m.index))}} + {m && {JavaScriptBlock(m[0])}} + {m && } + {!m && {VariableOutside(text)}} + + ); + + +} + +const VariableOutside = (text) => { + + var r = /\$\{((x(\d+)\.([\w\-\[\]\.]+))|(AKTO.changes_info\..*?))}/g; + let m = r.exec(text) + return ( + + {m && {text.substr(0, m.index)}} + {m && {m[0]}} + {m && {VariableOutside(text.substr(m.index + m[0].length, text.length))}} + {!m && {text}} + + ); + + +} + + +const JavaScriptBlock = (text) => { + + var r = /\$\{x(\d+)\.([\w\[\]\.]+)\}/g; + let m = r.exec(text) + return ( + + {m && {text.substr(0, m.index)}} + {m && {m[0]}} + {m && {JavaScriptBlock(text.substr(m.index + m[0].length, text.length))}} + {!m && {text}} + + ); + + +} + +export default TextFieldCloseable \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/Workflow.jsx b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/Workflow.jsx new file mode 100644 index 0000000000..a7e6f6b131 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/Workflow.jsx @@ -0,0 +1,397 @@ +import React, { useState, useEffect, useRef} from 'react' +import ReactFlow, { + Background, + getRectOfNodes +} from 'react-flow-renderer'; +import { faEye, faEyeSlash, faSave } from '@fortawesome/free-regular-svg-icons'; +import { faPlayCircle, faCalendarPlus, faArrowAltCircleDown} from '@fortawesome/free-regular-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import IconButton from "@mui/material/IconButton" + +import useStore from './store' +import StartNode from './StartNode.jsx'; +import BlankNode from './BlankNode.jsx'; +import EndNode from './EndNode.jsx'; + +import Drawer from '@mui/material/Drawer'; +import { AppBar } from '@material-ui/core'; +import { makeStyles } from "@material-ui/core/styles"; +import WorkflowResultsDrawer from './WorkflowResultsDrawer.jsx'; +import ScheduleBox from './ScheduleBox.jsx'; +import Menu from '@mui/material/Menu'; +import { saveAs } from 'file-saver' + + + +const onInit = (reactFlowInstance) => console.log('flow loaded:', reactFlowInstance); + +const nodeTypes = { startNode: StartNode, blankNode: BlankNode, endNode: EndNode }; + +const useStyles = makeStyles({ + drawer: { + position: "relative", + marginLeft: "auto", + "& .MuiBackdrop-root": { + display: "none" + }, + "& .MuiDrawer-paper": { + position: "absolute", + height: (props) => props.height, + } + } +}); + +const Workflow = ({apiCollectionId, defaultOpenResult}) => { + const nodes = useStore((state) => state.nodes) + const originalState = useStore((state) => state.originalState) + const edges = useStore((state) => state.edges) + const nodeEndpointMap = useStore((state) => state.nodeEndpointMap) + const currentSource = useStore((state) => state.currentSource) + + const onNodesChange = useStore((state) => state.onNodesChange) + const onEdgesChange = useStore((state) => state.onEdgesChange) + const onConnect = useStore((state) => state.onConnect) + + const setCurrentSource = useStore((state) => state.setCurrentSource) + const addNode = useStore((state) => state.addNode) + + const enteredNode = useStore(state => state.enteredNode) + const setEnteredNode = useStore(state => state.setEnteredNode) + + const testingRun = useStore(state => state.testingRun) + const fetchWorkflowTestingRun = useStore(state => state.fetchWorkflowTestingRun) + const deleteScheduledWorkflowTests = useStore(state => state.deleteScheduledWorkflowTests) + const scheduleWorkflowTest = useStore(state => state.scheduleWorkflowTest) + const downloadWorkflowAsJsonFn = useStore(state => state.downloadWorkflowAsJson) + + const onConnectStart = (event, {nodeId, handleType}) => { + setCurrentSource({x: event.screenX, y: event.screenY, nodeId, handleType}) + } + + const counter = useStore(state => state.counter) + const incrementCounter = useStore(state => state.incrementCounter) + + + const [open, setOpen] = useState(defaultOpenResult); + const [height, setHeight] = useState(0); + const [workflowTestResult, setWorkflowTestResult] = useState(null) + const [workflowTestingRun, setWorkflowTestingRun] = useState(null); + const [testRunning, setTestRunning] = useState(false); + const containerRef = useRef(); + const [scheduleBoxOpenFlag, setScheduleBoxOpenFlag] = useState(false) + const [anchorEl, setAnchorEl] = React.useState(null); + + const classes = useStyles({ height: height }); + + useEffect(() => { + open ? setHeight(containerRef.current.clientHeight - 64) : setHeight(0) + }, [open]); + + const getId = () => { + incrementCounter() + return 'x'+counter + } + + + + function getCycle(graph) { + let queue = Object.keys(graph).map( node => [node] ); + while (queue.length) { + const batch = []; + for (const path of queue) { + const parents = graph[path[0]] || []; + for (const node of parents) { + if (node === path[path.length-1]) return [node, ...path]; + batch.push([node, ...path]); + } + } + queue = batch; + } + } + + const cyclesPresent = (newEdge) => { + let graph = {} + for (const edge of [...edges, newEdge]) { + let source = edge.source + let target = edge.target + if (!graph[target]) { + graph[target] = [] + } + + graph[target].push(source) + } + return getCycle(graph) + } + + const onConnectStop = (event) => { + const refNode = nodes.find(x => x.id === currentSource.nodeId) + + if (enteredNode && enteredNode.id !== refNode.id) { + const newEdge = {source: refNode.id, target: enteredNode.id, id: getId(), selected: true, markerEnd: {type: 'arrow'}} + + if (refNode.id !== '3' && !cyclesPresent(newEdge) ) { + onConnect(newEdge) + } + + } else { + const deltaX = event.screenX - currentSource.x + const deltaY = event.screenY - currentSource.y + const lastNode = {...nodes[1]} + + const refNodeHeight = getRectOfNodes([refNode]).height + + lastNode.position = { + x: refNode.position.x + deltaX/zoom, + y: refNode.position.y + deltaY/zoom + refNodeHeight/2 + } + lastNode.hidden = false + lastNode.id = getId() + const newEdge = {source: refNode.id, target: lastNode.id, id: getId(), selected: true, markerEnd: {type: 'arrow'}} + addNode(lastNode, newEdge) + } + } + + const setZoom = useStore(state => state.setZoom) + const zoom = useStore(state => state.zoom) + const onMoveEnd = (event, viewport) => { + setZoom(viewport.zoom) + } + + const onNodeMouseEnter = (event, node) => { + setEnteredNode(node) + } + + const onNodeMouseLeave = (event, node) => { + setEnteredNode(null) + } + + const createWorkflowTest = useStore(state => state.createWorkflowTest) + const editWorkflowTest = useStore(state => state.editWorkflowTest) + const setOriginalState = useStore(state => state.setOriginalState) + const runWorkflowTest = useStore(state => state.runWorkflowTest) + const fetchWorkflowResult = useStore(state => state.fetchWorkflowResult) + const onSave = () => { + if (originalState.id) { + editWorkflowTest(originalState.id, nodes.map(JSON.stringify), edges.map(JSON.stringify), nodeEndpointMap).then((resp) => { + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Workflow saved", + color: 'green' + }) + }) + } else { + createWorkflowTest(nodes.map(JSON.stringify), edges.map(JSON.stringify), nodeEndpointMap, "DRAFT", apiCollectionId).then(resp => { + setOriginalState(resp.workflowTests[0]) + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Workflow saved", + color: 'green' + }) + }) + } + } + + const saveWorkflowFn = (recurring, startTimestamp) => { + if (!originalState.id) { + saveWorkflowEmitSnackbar() + return; + } + scheduleWorkflowTest(originalState.id, recurring, startTimestamp) + } + + const deleteWorkflowScheduleFn = () => { + if (!originalState.id) { + saveWorkflowEmitSnackbar() + return; + } + deleteScheduledWorkflowTests(originalState.id) + } + + const fetchResult = () => { + if (!originalState.id) { + saveWorkflowEmitSnackbar() + return; + } + return fetchWorkflowResult(originalState.id).then((resp) => { + if (!resp) return false + + setWorkflowTestingRun(resp["workflowTestingRun"]) + + let w = resp["workflowTestResult"] + + if (!w) return false + + let testResultMap = w["nodeResultMap"] + let keys = Object.keys(testResultMap) + + let finalResult = keys.map((x) => { + return {"key": x, ...testResultMap[x]} + }) + + setWorkflowTestResult(finalResult) + + return true + }) + } + + + const runTest = () => { + if (!originalState.id) { + saveWorkflowEmitSnackbar() + return; + } + + setTestRunning(true) + + runWorkflowTest(originalState.id).then((resp) => { + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Running test", + color: 'green' + }) + }) + + setWorkflowTestingRun(null) + setWorkflowTestResult(null) + + let interval = setInterval(() => { + fetchResult().then((result) => { + if (result) { + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Test completed", + color: 'green' + }) + setTestRunning(false) + clearInterval(interval) + } + }) + }, 5000) + + } + + const openScheduleBox= (event) => { + let v = scheduleBoxOpenFlag ? null : event.currentTarget + setAnchorEl(v); + setScheduleBoxOpenFlag(!scheduleBoxOpenFlag) + } + + const showResult = () => setOpen(!open); + + const downloadWorkflowAsJson = () => { + let workflowId = originalState.id + if (!workflowId) { + saveWorkflowEmitSnackbar() + return; + } + downloadWorkflowAsJsonFn(workflowId).then((resp) => { + let workflowTestJson = resp["workflowTestJson"] + var blob = new Blob([workflowTestJson], { + type: "application/json", + }); + const fileName = "workflow_"+workflowId+".json"; + saveAs(blob, fileName); + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: fileName + " downloaded !", + color: 'green' + }) + }) + } + + const saveWorkflowEmitSnackbar = () => { + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Please save the workflow first", + color: 'red' + }) + } + + React.useEffect(() => { + if (originalState.id) { + fetchResult() + fetchWorkflowTestingRun(originalState.id) + } + }, []); + + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; + +export default Workflow \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/WorkflowBuilder.jsx b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/WorkflowBuilder.jsx new file mode 100644 index 0000000000..8561be025f --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/WorkflowBuilder.jsx @@ -0,0 +1,61 @@ +import React, {useState, useEffect} from 'react'; +import { createTheme } from '@mui/material/styles'; + +import Workflow from './Workflow.jsx'; + +import useStore from './store' +import './start-node.css' + + +// use default theme +// const theme = createTheme(); + +// Or Create your Own theme: +const theme = createTheme({ + palette: { + secondary: { + main: '#47466A' + }, + primary: { + main: '#6200EA' + }, + warning: { + main: "rgba(243, 107, 107)" + } + } +}); + +const WorkflowBuilder = ({endpointsList, originalStateFromDb, fetchSampleDataFunc, createWorkflowTest, editWorkflowTest, editWorkflowNodeDetails, apiCollectionId, runWorkflowTest, fetchWorkflowResult, defaultOpenResult}) => { + const setOriginalState = useStore((state) => state.setOriginalState); + const setEndpointsList = useStore((state) => state.setEndpointsList); + const setUtilityFuncs = useStore((state) => state.setUtilityFuncs); + const originalState = useStore((state) => state.originalState) + const [renderNow, setRenderNow ]= useState(false) + + useEffect(() => { + setOriginalState(originalStateFromDb); + setUtilityFuncs(createWorkflowTest, editWorkflowTest, editWorkflowNodeDetails, runWorkflowTest, fetchWorkflowResult); + setRenderNow(true) // this was done because useEffect function inside workflow.jsx was called before this one's useEffect. This way it only loads after this. + }, [originalStateFromDb]); + + + useEffect(() => { + endpointsList.forEach(x => { + x.method = x.method.toUpperCase() + }) + setEndpointsList(endpointsList, fetchSampleDataFunc); + }, [endpointsList]); + + + if (renderNow) { + return ( + + ) + } else { + return ( +
Loading....
+ ) + } + +} +export default WorkflowBuilder \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/WorkflowResultsDrawer.jsx b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/WorkflowResultsDrawer.jsx new file mode 100644 index 0000000000..28ef3de394 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/WorkflowResultsDrawer.jsx @@ -0,0 +1,128 @@ +import React, { useState, useEffect, Component} from 'react' + +import Box from '@mui/material/Box'; +import Grid from '@mui/material/Grid'; +import func from "@/util/func"; +import TextFieldCloseable from './TextFieldCloseable.jsx' +import './start-node.css' +import LinearProgress from '@mui/material/LinearProgress'; + + +const WorkflowResultsDrawer = (props) => { + + let workflowTestResult = props["workflowTestResult"] + let workflowTestingRun = props["workflowTestingRun"] + + const [idx, setIdx] = useState(0); + let testRunning = props["testRunning"] || (workflowTestingRun && (workflowTestingRun["state"] === "SCHEDULED" || workflowTestingRun["state"] === "RUNNING")) + + const elemClass = (i, vulnerable) => { + let className = "results-node" + if (vulnerable) className += " result-node-vulnerable" + if (i === idx) className += " results-node-selected" + return className + } + + const elem = (element, i) => { + return ( + {setIdx(i)}} key={i} className={elemClass(i, element["vulnerable"])}> + {element["key"]} + + ) + } + + const elemList = () => { + if (!workflowTestResult) return null + return + {workflowTestResult.map((x, i) => elem(x, i))} + + } + + const testStatus = () => { + if (!workflowTestingRun) return null + let scheduleTimestamp = workflowTestingRun["scheduleTimestamp"] + return func.prettifyEpoch(scheduleTimestamp) + } + + const navBarContent = () => { + return ( + + + Test Results + + + {testStatus()} + + + {testRunning ? : null} + + + ) + } + + const mainContent = () => { + if (!workflowTestResult) return () + let currentNodeResult = workflowTestResult[idx] + if (!currentNodeResult) return (Result not found) + let message = currentNodeResult["message"] + let testErrors = currentNodeResult["errors"] + if (!message) { + return ( + {testErrors && testErrors.length > 0 ? testErrors[0] : "Invalid message"} + ) + } + let data = JSON.parse(message) + let request = data["request"] ? data["request"] : {} + let response = data["response"] ? data["response"] : {} + return ( +
+
[Request] URL
+
+ { } +
+
[Request] Query params
+
+ { } +
+
[Request] Headers
+
+ { } +
+
[Request] Payload
+
+ { } +
+
[Response] Headers
+
+ { } +
+
[Response] Status code
+
+ { } +
+
[Response] Payload
+
+ { } +
+
+ ) + } + + return ( + + + {navBarContent()} + + + {elemList()} + + + + {mainContent()} + + + + ) +} + +export default WorkflowResultsDrawer diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/start-node.css b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/start-node.css new file mode 100644 index 0000000000..486161f6dc --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/start-node.css @@ -0,0 +1,210 @@ +.start-node { + background: #fff; + border: 1px solid #47466A; + border-radius: 3px; + color: #47466A; + font-size: 12px; + padding: 10px; + text-align: center; + width: 200px; +} + +.start-node:hover { + box-shadow: 0px 0px 3px 3px #47466a3d; +} + +.red-boundary { + border: 1px solid #f44336 !important; +} + +.red-boundary:hover { + box-shadow: 0px 0px 3px 3px #f443363d; +} + +.green-boundary { + border: 1px solid #6200EA !important; +} + +.green-boundary:hover { + box-shadow: 0px 0px 3px 3px #6200EA3d !important; +} + +.MuiInput-input { + color: #47466A !important; + font-size: 12px !important; +} + +.MuiFormControl-root { + margin: 0px !important; +} + +.MuiIconButton-root { + padding-top: 8px !important; +} + +.MuiButton-root { + background-color: #6200EA !important; + margin: 0 12px 12px 0 !important; +} + +.MuiDialog-paperScrollPaper { + min-width: 850px; +} + +.variable-name { + position: absolute; + top: -8px; + left: 0px; + font-size: 10px !important; + color: #6200EA !important; +} + +.autocomplete-options { + font-size: 12px !important; + color: #47466A !important; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.autocomplete-options:hover { + text-overflow: clip; + white-space: normal; + word-break: break-all; +} + +.GET { + color: #61affe !important; +} + +.POST { + color: #49cc90 !important; +} + +.PUT { + color: #fca130 !important; +} + +.DELETE { + color: #f93e3e !important; +} + +.PATCH { + color: #50e3c2 !important; +} + +.HEAD { + color: #9012fe !important; +} + +.OPTIONS { + color: #0d5aa7 !important; +} + +[method="GET"] { + color: #61affe !important; +} + +[method="POST"] { + color: #49cc90 !important; +} + +[method="PUT"] { + color: #fca130 !important; +} + +[method="DELETE"] { + color: #f93e3e !important; +} + +[method="PATCH"] { + color: #50e3c2 !important; +} + +[method="HEAD"] { + color: #9012fe !important; +} + +[method="OPTIONS"] { + color: #0d5aa7 !important; +} + +.react-flow__attribution a { + display: none; +} + + +.react-flow__edge-path:hover { + stroke-width: 2px; + filter: drop-shadow(3px 5px 2px #47466A3d); + cursor: pointer; +} + +.primary-btn { + color: #6200EA; + font-size: 12px; +} + +.MuiPaper-root { + color: #47466A !important +} + +.request-editor { + font-size: 12px !important; + font-family: monospace !important; + color: #47466A !important; + letter-spacing: unset !important; + line-height: 23px !important; + word-wrap: break-word !important; +} + +.workflow-button { + color: #6200EA !important; + font-weight: 600 !important; +} + +.request-editor-matched { + color: darkblue !important; + font-weight: 600 !important; +} + +.request-editor-variable { + color: #6200EA !important; + font-weight: 600 !important; +} + +.request-title { + padding-top: 12px; + font-size: 13px; + color: #47466A; + font-family: 'Poppins'; + font-weight: 500; +} + +.results-node { + margin-top: 24px; + margin-bottom: 24px; + margin-left: 8px; + margin-right: 8px; + background: rgba(71,70,106,.03); + padding: 0px; + text-align: center; + line-height: 30px; + border-radius: 4px; + cursor: pointer; + overflow: hidden +} + +.results-node:hover { + text-overflow: clip; + white-space: normal; + word-break: break-all; +} + +.result-node-vulnerable { + color: red +} + +.results-node-selected { + font-weight: bold; +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/store.js b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/store.js new file mode 100644 index 0000000000..e2811cfebe --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/react/store.js @@ -0,0 +1,194 @@ +import create from 'zustand'; +import { + addEdge, + applyNodeChanges, + applyEdgeChanges, +} from 'react-flow-renderer'; +import api from '../../../observe/inventory/api'; + +const useStore = create((set, get) => ({ + endpointsList: [], + fetchSampleDataFunc: null, + nodes: [], + edges: [], + currentSource: null, + zoom: 1, + enteredNode: null, + nodeEndpointMap: {}, + counter: 1, + createWorkflowTest: null, + editWorkflowTest: null, + editWorkflowNodeDetails: null, + originalState: null, + runWorkflowTest: null, + fetchWorkflowResult: null, + testingRun: null, + setUtilityFuncs: (newCreateWorkflowTest, newEditWorkflowTest, newEditWorkflowNodeDetails, runWorkflowTest, fetchWorkflowResult) => { + set({ + createWorkflowTest: newCreateWorkflowTest, + editWorkflowTest: newEditWorkflowTest, + editWorkflowNodeDetails: newEditWorkflowNodeDetails, + runWorkflowTest: runWorkflowTest, + fetchWorkflowResult: fetchWorkflowResult + }) + }, + setOriginalState: (originalStateFromDb) => { + let nodes = originalStateFromDb.nodes.map(x => JSON.parse(x)) + let counter = 0; + nodes.forEach((x) => { + let id = x["id"]; + if (id.startsWith("x")) { + let numAsString = id.slice(1,x.length) + let num = parseInt(numAsString) + counter = num > counter ? num : counter + } + }) + set({ + originalState: originalStateFromDb, + counter: counter+1, + nodes: nodes, + edges: originalStateFromDb.edges.map(x => JSON.parse(x)), + nodeEndpointMap: {...originalStateFromDb.mapNodeIdToWorkflowNodeDetails} + }) + }, + incrementCounter: () => { + set({ + counter: (get().counter + 1) + }) + }, + addNodeEndpoint: (nodeId, endpointData) => { + let ret = get().nodeEndpointMap + ret[nodeId] = endpointData + set({ + nodeEndpointMap: ret + }) + }, + + setApiType: (nodeId, apiType) => { + let ret = get().nodeEndpointMap + let endpointData = ret[nodeId] + if (!endpointData) return + endpointData["type"] = apiType + set({ + nodeEndpointMap: {...ret} + }) + }, + + setRedirect: (nodeId, overrideRedirect) => { + let ret = get().nodeEndpointMap + let endpointData = ret[nodeId] + if (!endpointData) return + endpointData["overrideRedirect"] = overrideRedirect + set({ + nodeEndpointMap: {...ret} + }) + }, + + setSleep: (nodeId, waitInSeconds) => { + let ret = get().nodeEndpointMap + let endpointData = ret[nodeId] + if (!endpointData) return + endpointData["waitInSeconds"] = waitInSeconds + set({ + nodeEndpointMap: {...ret} + }) + }, + + setValidatorCode: (nodeId, testValidatorCode) => { + let ret = get().nodeEndpointMap + let endpointData = ret[nodeId] + if (!endpointData) return + endpointData["testValidatorCode"] = testValidatorCode + set({ + nodeEndpointMap: {...ret} + }) + }, + + setEndpointsList: (newList, newFetchSampleDataFunc) => { + set({ + endpointsList: newList, + fetchSampleDataFunc: newFetchSampleDataFunc + }) + }, + setZoom: (newZoom) => { + set({ + zoom: newZoom + }) + }, + setEnteredNode: (newNode) => { + set({ + enteredNode: newNode + }) + }, + addNode: (newNode, newEdge) => { + set({ + nodes: [ + ...get().nodes, + newNode + ] + }) + set({ + edges: addEdge( + newEdge, + get().edges + ) + }) + + }, + setCurrentSource: (newSourceNode) => { + set({ + currentSource: newSourceNode + }) + }, + onNodesChange: (changes) => { + set({ + nodes: applyNodeChanges(changes, get().nodes) + }); + }, + onEdgesChange: (changes) => { + set({ + edges: applyEdgeChanges(changes, get().edges) + }); + }, + onConnect: (connection) => { + set({ + edges: addEdge(connection, get().edges) + }); + }, + + fetchWorkflowTestingRun: (workflowId) => { + api.fetchWorkflowTestingRun(workflowId).then((resp) => { + if (resp && resp.testingRuns) { + set({ + testingRun: resp.testingRuns[0] + }) + } + }) + }, + + scheduleWorkflowTest: (id, recurringDaily, startTimestamp) => { + api.scheduleWorkflowTest(id, recurringDaily, startTimestamp).then((resp) => { + if (resp && resp.testingRuns) { + set({ + testingRun: resp.testingRuns[0] + }) + } + }) + }, + + deleteScheduledWorkflowTests: (id) => { + api.deleteScheduledWorkflowTests(id).then((resp) => { + set({ + testingRun: null + }) + }) + }, + + downloadWorkflowAsJson: (id) => { + return api.downloadWorkflowAsJson(id) + } + +})); + + +export default useStore; diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/TestRoles.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/TestRoles.vue new file mode 100644 index 0000000000..2d650bf166 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/TestRoles.vue @@ -0,0 +1,122 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/api.js b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/api.js new file mode 100644 index 0000000000..aee2a8669c --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/api.js @@ -0,0 +1,40 @@ +import request from '@/util/request' + +export default { + fetchTestRoles() { + return request({ + url: '/api/fetchTestRoles', + method: 'post', + data: {} + }).then((resp) => { + return resp + }) + }, + addTestRoles (roleName, andConditions, orConditions) { + return request({ + url: '/api/addTestRoles', + method: 'post', + data: {roleName, andConditions, orConditions} + }).then((resp) => { + return resp + }) + }, + updateTestRoles (roleName, andConditions, orConditions) { + return request({ + url: '/api/updateTestRoles', + method: 'post', + data: {roleName, andConditions, orConditions} + }).then((resp) => { + return resp + }) + }, + fetchCollectionWiseApiEndpoints (apiCollectionId) { + return request({ + url: '/api/fetchCollectionWiseApiEndpoints', + method: 'post', + data: {apiCollectionId} + }).then((resp) => { + return resp + }) + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/components/TestRoleCard.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/components/TestRoleCard.vue new file mode 100644 index 0000000000..6d539af1a6 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/components/TestRoleCard.vue @@ -0,0 +1,78 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/components/TestRoleConditionsTable.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/components/TestRoleConditionsTable.vue new file mode 100644 index 0000000000..737f6b8a02 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/components/TestRoleConditionsTable.vue @@ -0,0 +1,201 @@ + + + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/components/TestRolesConfigDetails.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/components/TestRolesConfigDetails.vue new file mode 100644 index 0000000000..9cd2184c09 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/components/TestRolesConfigDetails.vue @@ -0,0 +1,339 @@ + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/store/module.js new file mode 100644 index 0000000000..6095dd7783 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/test_roles/store/module.js @@ -0,0 +1,100 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import api from '../api' + + +Vue.use(Vuex) + +const state = { + loading: false, + testRoles: [], + selectedRole: {}, + conditions: [], + createNew : false, + collectionWiseApiInfoKeyMap: {} +} + +const test_roles = { + namespaced: true, + state: state, + mutations: { + EMPTY_STATE (state) { + state.loading = false + state.testRoles = [] + state.selectedRole = {} + }, + SAVE_TEST_ROLES(state, {testRoles}) { + state.testRoles = testRoles + }, + SAVE_SELECTED_ROLE(state, {selectedRole}) { + state.selectedRole = selectedRole + }, + updateTestRoleWithNewRole(state, {selectedRole}) { + state.testRoles.push(selectedRole) + }, + SAVE_CREATE_NEW(state, {createNew}) { + state.createNew = createNew; + }, + SAVE_CONDITIONS (state, {conditions}) { + state.conditions = conditions + }, + SAVE_COLLECTION_WISE_INFO_KEY_MAP(state, {collectionWiseApiInfoKeyMap}) { + state.collectionWiseApiInfoKeyMap = collectionWiseApiInfoKeyMap + } + }, + actions: { + emptyState({ commit }) { + commit('EMPTY_STATE') + }, + loadTestRoles({commit}) { + state.loading = true + return api.fetchTestRoles().then((resp) => { + commit('SAVE_TEST_ROLES', resp) + state.loading = false + }).catch((resp) => { + state.loading = false + }) + }, + addTestRoles ({commit}, {roleName, andConditions, orConditions}) { + state.loading = true + api.addTestRoles(roleName, andConditions, orConditions).then((resp) => { + commit('SAVE_SELECTED_ROLE', resp) + commit('updateTestRoleWithNewRole', resp) + commit('SAVE_CREATE_NEW', {createNew: false}) + state.loading = false + + }).catch(() => { + state.loading = false + }) + }, + updateTestRoles ({commit}, {roleName, andConditions, orConditions}) { + state.loading = true + api.updateTestRoles(roleName, andConditions, orConditions).then((resp) => { + state.loading = false + + }).catch(() => { + state.loading = false + }) + }, + async fetchApiInfoKeyForCollection ({commit}, {collectionId}) { + if (!(collectionId in state.collectionWiseApiInfoKeyMap)) { + state.loading = true + await api.fetchCollectionWiseApiEndpoints(parseInt(collectionId)).then((resp) => { + let collectionWiseApiInfoKeyMap = {} + collectionWiseApiInfoKeyMap[collectionId] = resp['listOfEndpointsInCollection'] + commit('SAVE_COLLECTION_WISE_INFO_KEY_MAP', {collectionWiseApiInfoKeyMap}) + this.loading=false + }).catch(() => { + this.loading = false + }) + } + } + }, + getters: { + getLoading: (state) => state.loading, + getTestRoles: (state) => state.testRoles, + getSelectedRole: (state) => state.selectedRole + } +} + +export default test_roles \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/token/LoginStepBuilder.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/token/LoginStepBuilder.vue new file mode 100644 index 0000000000..39b40a4091 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/token/LoginStepBuilder.vue @@ -0,0 +1,396 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/components/token/TokenAutomation.vue b/apps/dashboard/web/src/apps/dashboard/views/testing/components/token/TokenAutomation.vue new file mode 100644 index 0000000000..9e8a70f1be --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/components/token/TokenAutomation.vue @@ -0,0 +1,447 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/testing/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/testing/store/module.js new file mode 100644 index 0000000000..647d894e96 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/testing/store/module.js @@ -0,0 +1,103 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import api from '../api' + + +Vue.use(Vuex) + +const state = { + loading: false, + testingRuns: [], + pastTestingRuns: [], + authMechanism: null, + testingRunResults: [] +} + +const testing = { + namespaced: true, + state: state, + mutations: { + EMPTY_STATE (state) { + state.loading = false + state.testingRuns = [] + state.authMechanism = null + state.testingRunResults = [] + state.pastTestingRuns = [] + }, + SAVE_DETAILS (state, {authMechanism, testingRuns}) { + state.authMechanism = authMechanism + state.testingRuns = testingRuns + }, + SAVE_PAST_DETAILS (state, {testingRuns}) { + state.pastTestingRuns = testingRuns + }, + SAVE_TESTING_RUNS (state, {testingRuns}) { + state.testingRuns = testingRuns + }, + SAVE_AUTH_MECHANISM (state, {key, value, location}) { + state.authMechanism.authParams[0] = {key, value, where: location} + }, + SAVE_TESTING_RUN_RESULTS(state, {testingRunResults}) { + state.testingRunResults = testingRunResults + } + }, + actions: { + emptyState({commit}, payload, options) { + commit('EMPTY_STATE', payload, options) + }, + loadTestingDetails({commit}, {startTimestamp, endTimestamp}) { + commit('EMPTY_STATE') + state.loading = true + return api.fetchActiveTestingDetails().then((resp) => { + commit('SAVE_DETAILS', resp) + + api.fetchPastTestingDetails({startTimestamp, endTimestamp}).then(resp2 => { + commit('SAVE_PAST_DETAILS', resp2) + }).catch(() => { + + }) + + state.loading = false + }).catch(() => { + state.loading = false + }) + }, + startTestForCollection({commit}, {apiCollectionId, testName}) { + return api.startTestForCollection(apiCollectionId, testName).then((resp) => { + commit('SAVE_TESTING_RUNS', resp) + }) + }, + scheduleTestForCollection({commit}, {apiCollectionId, startTimestamp, recurringDaily, selectedTests, testName,testRunTime, maxConcurrentRequests} ) { + return api.scheduleTestForCollection(apiCollectionId, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests).then((resp) => { + commit('SAVE_TESTING_RUNS', resp) + }) + }, + startTestForCustomEndpoints({commit}, {apiInfoKeyList, testName}) { + return api.startTestForCustomEndpoints(apiInfoKeyList, testName).then((resp) => { + commit('SAVE_TESTING_RUNS', resp) + }) + }, + scheduleTestForCustomEndpoints({commit}, {apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests} ) { + return api.scheduleTestForCustomEndpoints(apiInfoKeyList, startTimestamp, recurringDaily, selectedTests, testName, testRunTime, maxConcurrentRequests).then((resp) => { + commit('SAVE_TESTING_RUNS', resp) + }) + }, + addAuthMechanism({commit}, {type, requestData, authParamData}) { + return api.addAuthMechanism(type, requestData, authParamData).then(resp => { + commit('SAVE_AUTH_MECHANISM', resp) + }) + }, + loadTestingRunResults({commit}) { + api.fetchTestingRunResults().then(resp => { + commit('SAVE_TESTING_RUN_RESULTS', resp) + }) + } + }, + getters: { + getLoading: (state) => state.loading, + getTestingRuns: (state) => state.testingRuns, + getAuthMechanism: (state) => state.authMechanism + } +} + +export default testing \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/Today.vue b/apps/dashboard/web/src/apps/dashboard/views/today/Today.vue new file mode 100644 index 0000000000..963c4c052e --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/Today.vue @@ -0,0 +1,115 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/api.js b/apps/dashboard/web/src/apps/dashboard/views/today/api.js new file mode 100644 index 0000000000..2bb60f297b --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/api.js @@ -0,0 +1,53 @@ +import request from '@/util/request' + +export default { + saveContentAndFetchAuditResults(apiSpec) { + return request({ + url: '/api/saveContentAndFetchAuditResults', + method: 'post', + data: { + apiSpec: apiSpec.content, + filename: apiSpec.filename + } + }) + }, + + fetchAuditResults() { + return request({ + url: '/api/fetchAuditResults', + method: 'post', + data: {} + }) + }, + + saveTestConfig({authProtocolSettings, testEnvSettings, authProtocolSpecs, contentId}) { + return request({ + url: '/api/saveTestConfig', + method: 'post', + data: { + authProtocolSettings, + testEnvSettings, + authProtocolSpecs, + contentId + } + }) + }, + + fetchTestResults({attemptIds}) { + return request({ + url: '/api/fetchTestResults', + method: 'post', + data: { + attemptIds + } + }) + }, + + getAPICatalog() { + return request({ + url: '/api/getAPICatalog', + method: 'get' + }) + } + +} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/components/APIDetails.vue b/apps/dashboard/web/src/apps/dashboard/views/today/components/APIDetails.vue new file mode 100644 index 0000000000..be46eb98de --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/components/APIDetails.vue @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/components/APISecurityAudit.vue b/apps/dashboard/web/src/apps/dashboard/views/today/components/APISecurityAudit.vue new file mode 100644 index 0000000000..73bc4ea5a6 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/components/APISecurityAudit.vue @@ -0,0 +1,55 @@ + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/components/APISummary.vue b/apps/dashboard/web/src/apps/dashboard/views/today/components/APISummary.vue new file mode 100644 index 0000000000..a32e8444e8 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/components/APISummary.vue @@ -0,0 +1,191 @@ + + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/components/APITestReport.vue b/apps/dashboard/web/src/apps/dashboard/views/today/components/APITestReport.vue new file mode 100644 index 0000000000..46bfdae647 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/components/APITestReport.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/components/AuthProtocolSetup.vue b/apps/dashboard/web/src/apps/dashboard/views/today/components/AuthProtocolSetup.vue new file mode 100644 index 0000000000..a6504c83cc --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/components/AuthProtocolSetup.vue @@ -0,0 +1,147 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/components/SpecFileDialog.vue b/apps/dashboard/web/src/apps/dashboard/views/today/components/SpecFileDialog.vue new file mode 100644 index 0000000000..9f976bcfe5 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/components/SpecFileDialog.vue @@ -0,0 +1,93 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/components/TestEnvSetup.vue b/apps/dashboard/web/src/apps/dashboard/views/today/components/TestEnvSetup.vue new file mode 100644 index 0000000000..d6151851e7 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/components/TestEnvSetup.vue @@ -0,0 +1,96 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/components/TestingSetupModal.vue b/apps/dashboard/web/src/apps/dashboard/views/today/components/TestingSetupModal.vue new file mode 100644 index 0000000000..d2f33053fb --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/components/TestingSetupModal.vue @@ -0,0 +1,110 @@ + + + + + + + + diff --git a/apps/dashboard/web/src/apps/dashboard/views/today/store/module.js b/apps/dashboard/web/src/apps/dashboard/views/today/store/module.js new file mode 100644 index 0000000000..fd5938d4c3 --- /dev/null +++ b/apps/dashboard/web/src/apps/dashboard/views/today/store/module.js @@ -0,0 +1,126 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import api from '../api' + + +Vue.use(Vuex) + +var state = { + loading: false, + fetchTs: 0, + content: null, + errors: null, + auditId: null, + contentId: null, + endpoint: '', + description: '', + numberOfPaths: 0, + authProtocol: null, + filename: '', + testDetails: null, + testResults: null +} + +const today = { + namespaced: true, + state: state, + mutations: { + EMPTY_STATE (state) { + state.loading = false + state.fetchTs = 0 + content = null + errors = null + auditId = null + contentId = null + endpoint = '' + description = '' + numberOfPaths = 0 + authProtocol = null + filename = '' + testDetails = null + testResults = null + }, + SAVE_AUDIT_RESULTS (state, {auditId, contentId, errors, endpoint, description, numberOfPaths, authProtocol}) { + state.auditId = auditId + state.contentId = contentId + state.errors = errors + state.endpoint = endpoint + state.description = description + state.numberOfPaths = numberOfPaths + state.authProtocol = authProtocol + }, + SAVE_TEST_DETAILS (state, testDetails) { + state.testDetails = testDetails + }, + SAVE_TEST_RESULTS (state, attempts) { + state.testResults = attempts + } + }, + actions: { + saveContentAndFetchAuditResults({ commit, dispatch, state }, {content, filename}) { + state.loading = true + api.saveContentAndFetchAuditResults({content, filename}).then(resp => { + let {content, filename} = resp.data + state.filename = filename + state.content = content + let endpoint = content.servers[0].url + let description = content.info.description + let numberOfPaths = Object.keys(content.paths|| {}).length + let authProtocol = content.components.securitySchemes + + commit('SAVE_AUDIT_RESULTS', {...resp.data, description, endpoint, numberOfPaths, authProtocol }) + state.loading = false + }).catch((err) => { + state.loading = false + }) + }, + fetchAuditResults({ commit, dispatch, state }) { + state.loading = true + api.fetchAuditResults().then(resp => { + let {content, filename} = resp.data + + if (content) { + state.filename = filename + state.content = content + let endpoint = content.servers[0].url + let description = content.info.description + let numberOfPaths = Object.keys(content.paths|| {}).length + let authProtocol = content.components.securitySchemes + + commit('SAVE_AUDIT_RESULTS', {...resp.data, description, endpoint, numberOfPaths, authProtocol }) + } + state.loading = false + }).catch((err) => { + state.loading = false + }) + }, + saveTestConfig({commit, state}, {authProtocolSettings, testEnvSettings, authProtocolSpecs}) { + state.loading = true + api.saveTestConfig({authProtocolSettings, testEnvSettings, authProtocolSpecs, contentId: state.contentId}).then(resp => { + commit('SAVE_TEST_DETAILS', resp.data) + state.loading = false + }).catch( (e) => { + state.loading = false + }) + }, + fetchTestResults({commit, state}) { + api.fetchTestResults({attemptIds: state.testDetails.attempts}).then(resp => { + commit('SAVE_TEST_RESULTS', resp.data) + }).catch((e) => { + }) + }, + emptyState({commit, dispatch}, payload, options) { + commit('EMPTY_STATE', payload, options) + } + }, + getters: { + getFetchTs: (state) => state.fetchTs, + getLoading: (state) => state.loading, + getContent: (state) => state.content, + getErrors: (state) => state.errors, + getContentId: (state) => state.contentId, + getAuditId: (state) => state.auditId + } +} + +export default today \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/login/App.vue b/apps/dashboard/web/src/apps/login/App.vue new file mode 100644 index 0000000000..906126f1e7 --- /dev/null +++ b/apps/dashboard/web/src/apps/login/App.vue @@ -0,0 +1,186 @@ + + + + + diff --git a/apps/dashboard/web/src/apps/login/LoginFields.vue b/apps/dashboard/web/src/apps/login/LoginFields.vue new file mode 100644 index 0000000000..af79690855 --- /dev/null +++ b/apps/dashboard/web/src/apps/login/LoginFields.vue @@ -0,0 +1,136 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/login/store/index.js b/apps/dashboard/web/src/apps/login/store/index.js new file mode 100644 index 0000000000..989ad19baf --- /dev/null +++ b/apps/dashboard/web/src/apps/login/store/index.js @@ -0,0 +1,3 @@ +import auth from './modules/auth/module' + +export {auth} \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/login/store/modules/auth/api.js b/apps/dashboard/web/src/apps/login/store/modules/auth/api.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/dashboard/web/src/apps/login/store/modules/auth/module.js b/apps/dashboard/web/src/apps/login/store/modules/auth/module.js new file mode 100644 index 0000000000..0d70bc1bfa --- /dev/null +++ b/apps/dashboard/web/src/apps/login/store/modules/auth/module.js @@ -0,0 +1,93 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import request from '@/util/request' +import apiNav from "@/apps/dashboard/nav/api"; + +Vue.use(Vuex) + +var state = { + access_token: null, + expires_in: 3600, + token_type: 'bearer', + username: window.USER_NAME, + avatar: window.AVATAR, + accounts: window.ACCOUNTS || [], + activeAccount: window.ACTIVE_ACCOUNT, + users: window.USERS || [] +} + +const auth = { + namespaced: true, + state: state, + mutations: { + SET_LOGIN(state, { accessToken, expires_in }) { + state.access_token = accessToken + state.expires_in = expires_in + }, + SET_ACCESS_TOKEN(state, token) { + state.access_token = token + }, + SET_LOGIN_PROFILE(state, payload) { + state.username = payload.username + state.avatar = payload.avatar + state.accounts = payload.accounts + state.activeAccount = payload.activeAccount + state.users = payload.users + } + }, + actions: { + login({ commit, dispatch }, { username, password, accountId }) { + return request({ + url: '/auth/login', + method: 'post', + data: { + username, + password + } + }).then((resp, a, b, c) => { + var redirectLink = '/dashboard/testing' + if (resp.loginResult && resp.loginResult.redirect) { + redirectLink = resp.loginResult.redirect + } else { + var redirectLink = new URLSearchParams(window.location.search).get('redirect_uri') || '/dashboard/testing' + if (!redirectLink.startsWith('/dashboard/')) { + redirectLink = '/dashboard/testing' + } + } + window.location.href = redirectLink + }) + }, + logout({ commit }) { + commit('SET_ACCESS_TOKEN', null) + }, + // get current login user info + + fetchProfile({ commit }, accountId) { + return request({ + url: '/api/me', + method: 'post', + data: { + type: "all", + accountId: accountId + } + }) + }, + + addNewTeam({commit}, {name}) { + return apiNav.createNewTeam(name).then(resp => { + commit('ADD_TEAM', {name, id: resp.id}) + return resp + }) + } + }, + getters: { + getAccessToken: (state) => state.access_token, + getAvatar: (state) => state.avatar, + getUsername: (state) => state.username, + getAccounts: (state) => state.accounts, + getActiveAccount: (state) => state.activeAccount, + getActiveAccountId: (state) => (state.activeAccount && state.activeAccount.accountId) || 0 + } +} + +export default auth \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/main/App.vue b/apps/dashboard/web/src/apps/main/App.vue new file mode 100644 index 0000000000..e2cdb56b60 --- /dev/null +++ b/apps/dashboard/web/src/apps/main/App.vue @@ -0,0 +1,233 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/main/main.js b/apps/dashboard/web/src/apps/main/main.js new file mode 100644 index 0000000000..96e4072432 --- /dev/null +++ b/apps/dashboard/web/src/apps/main/main.js @@ -0,0 +1,19 @@ +import Vue from 'vue' +import App from './App.vue' +import vuetify from '@/plugins/vuetify' +import router from './router' +import store from './store/module' + +import { VuePlugin } from "vuera"; +Vue.use(VuePlugin); + +// Storing the access token from login.jsp +store.state.auth.access_token = window.ACCESS_TOKEN; + +new Vue({ + el: '#app', + vuetify, + router, + store, + render: h => h(App) +}) diff --git a/apps/dashboard/web/src/apps/main/router/index.js b/apps/dashboard/web/src/apps/main/router/index.js new file mode 100644 index 0000000000..be32151c60 --- /dev/null +++ b/apps/dashboard/web/src/apps/main/router/index.js @@ -0,0 +1,230 @@ +import Vue from 'vue' +import Router from 'vue-router' +const PageLogin = () => import( '@/apps/login/App') +const PageDashboard = () => import( '@/apps/dashboard/App') +const PageToday = () => import( "@/apps/dashboard/views/today/Today") +const PageMarketplace = () => import( "@/apps/dashboard/views/marketplace/PageMarketplace") +const PageQuickStart = () => import( "@/apps/dashboard/views/quickstart/PageQuickStart") +const PageTesting = () => import( "@/apps/dashboard/views/testing/PageTesting") +const PageIssues = () => import( "@/apps/dashboard/views/issues/PageIssues") +const TestingRunsTable = () => import( "@/apps/dashboard/views/testing/components/TestingRunsTable") +const TestingRunResults = () => import( "@/apps/dashboard/views/testing/components/TestingRunResults") +const CreateTestingRun = () => import( "@/apps/dashboard/views/testing/components/CreateTestingRun") +import store from '@/apps/main/store/module' +const PageSignup = () => import("@/apps/signup/PageSignup") +const PageSettings = () => import("@/apps/dashboard/views/settings/PageSettings") +const Observe = () => import( "@/apps/dashboard/views/observe/inventory/Observe") +const Inventory = () => import("@/apps/dashboard/views/observe/inventory/Inventory") +const APIParameters = () => import("@/apps/dashboard/views/observe/inventory/components/APIParameters") +const APIEndpoints = () => import("@/apps/dashboard/views/observe/inventory/components/APIEndpoints") +const APICollections = () => import("@/apps/dashboard/views/observe/collections/APICollections") +const SensitiveData = () => import("@/apps/dashboard/views/observe/sensitive/SensitiveData") +const ApiChanges = () => import("@/apps/dashboard/views/observe/changes/Changes") +const ParamState = () => import("@/apps/dashboard/views/observe/misc/ParamState") +const MPTestCategory = () => import("@/apps/dashboard/views/marketplace/components/MPTestCategory") + +Vue.use(Router) + +function getId (route) { + return { + id: +route.params.id + } +} +const router = new Router({ + mode: 'history', + base: process.env.BASE_URL, + routes: [ + { + path: '/', + redirect: 'login', + component: PageLogin + }, + { + path: '/login', + name: 'login', + component: PageLogin + }, + { + path: '/signup', + name: 'signup', + component: PageSignup + }, + { + path: '/dashboard', + name: 'dashboard', + component: PageDashboard, + redirect: '/dashboard/testing', + beforeEnter (to, from, next) { + store.dispatch('collections/loadAllApiCollections').then(() => next()).catch(() => next()) + }, + children: [ + { + path: 'quick-start', + name: 'quick-start', + component: PageQuickStart + }, + { + path: 'testing', + name: 'testing', + redirect: 'testing/active', + components: { + default: PageTesting + }, + children: [ + { + path: 'active', + name: 'testResults', + component: TestingRunsTable, + props: route => ({ + active: true + }) + }, + { + path: 'completed', + name: 'testResults', + component: TestingRunsTable, + props: route => ({ + active: false + }) + }, + { + path: 'inactive', + name: 'inactiveTestResults', + component: TestingRunsTable, + props: route => ({ + active: false + }) + }, + { + path: ':testingRunHexId/results', + name: 'testResults', + component: TestingRunResults, + props: route => ({ + testingRunHexId: route.params.testingRunHexId, + key: route.params.testingRunHexId + }) + }, + { + path: 'create/:apiCollectionId', + name: 'createFromApiCollection', + component: CreateTestingRun + + + } + ] + }, + { + path: 'issues', + name: 'issues', + component: PageIssues + }, + { + path: 'settings', + name: 'settings', + components: { + default: PageSettings + } + }, + { + path: 'observe', + name: 'observe', + component: Observe, + children:[ + { + path: 'inventory', + name: 'inventory', + component: Inventory, + children: [ + { + path:'', + name:'default', + component: APICollections + }, + { + path:':apiCollectionId', + name:'apiCollection', + component: APIEndpoints, + props: route => ({ + apiCollectionId: +route.params.apiCollectionId + }) + }, + { + path:':apiCollectionId/:urlAndMethod', + name:'apiCollection/urlAndMethod', + component: APIParameters, + props: route => ({ + urlAndMethod: atob(route.params.urlAndMethod), + apiCollectionId: +route.params.apiCollectionId + }) + } + ] + }, + { + path: 'changes', + name: 'changes', + component: ApiChanges, + props: route => ({ + openTab: route.query.tab === "parameters" ? "parameters" : "endpoints", + defaultStartTimestamp: +route.query.start, + defaultEndTimestamp: +route.query.end + }) + }, + { + path: 'sensitive', + name: 'sensitive', + component: SensitiveData + }, + { + path: 'param_state', + name: 'param_state', + component: ParamState, + } + ] + }, + { + path: 'library', + name: 'library', + components: { + default: PageMarketplace + }, + children: [ + { + path: 'custom/:category_id', + name: 'customCategory', + component: MPTestCategory, + props: route => ({ + categoryType: "custom", + categoryId: route.params.category_id + }) + }, + { + path: 'default/:category_id', + name: 'defaultCategory', + component: MPTestCategory, + props: route => ({ + categoryType: "default", + categoryId: route.params.category_id + }) + } + ] + } + ] + }, + ] +}) + + +router.beforeEach((to, from, next) => { + if (window._AKTO) { + window._AKTO.$emit('HIDE_SNACKBAR') + } + + if (to.name === 'signup' || to.name === 'login') { + store.commit('auth/SET_ACCESS_TOKEN',null) + } + if (window.mixpanel && window.mixpanel.track) + window.mixpanel.track(to.name) + next() +}) + +export default router \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/main/store/module.js b/apps/dashboard/web/src/apps/main/store/module.js new file mode 100644 index 0000000000..6950fd0b58 --- /dev/null +++ b/apps/dashboard/web/src/apps/main/store/module.js @@ -0,0 +1,40 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import auth from '@/apps/login/store/modules/auth/module' +import team from '@/apps/dashboard/views/settings/store/module' +import today from '@/apps/dashboard/views/today/store/module' +import testing from '@/apps/dashboard/views/testing/store/module' +import inventory from '@/apps/dashboard/views/observe/inventory/store/module' +import changes from '@/apps/dashboard/views/observe/changes/store/module' +import sensitive from '@/apps/dashboard/views/observe/sensitive/store/module' +import collections from '@/apps/dashboard/views/observe/collections/store/module' +import data_types from '@/apps/dashboard/views/settings/components/data_types/store/module' +import tag_configs from '@/apps/dashboard/views/settings/components/tag_configs/store/module' +import auth_types from '../../dashboard/views/settings/components/auth_types/store/module' +import issues from '@/apps/dashboard/views/issues/store/module' +import test_roles from '@/apps/dashboard/views/testing/components/test_roles/store/module' +import marketplace from '@/apps/dashboard/views/marketplace/store/module' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + namespaced: true, + modules: { + auth, + team, + today, + inventory, + collections, + changes, + sensitive, + data_types, + tag_configs, + auth_types, + testing, + issues, + test_roles, + marketplace + } +}) + +export default store \ No newline at end of file diff --git a/apps/dashboard/web/src/apps/signup/PageSignup.vue b/apps/dashboard/web/src/apps/signup/PageSignup.vue new file mode 100644 index 0000000000..ac43c7a1c8 --- /dev/null +++ b/apps/dashboard/web/src/apps/signup/PageSignup.vue @@ -0,0 +1,213 @@ + + + + + \ No newline at end of file diff --git a/apps/dashboard/web/src/assets/brand_name.svg b/apps/dashboard/web/src/assets/brand_name.svg new file mode 100644 index 0000000000..6927c661f8 --- /dev/null +++ b/apps/dashboard/web/src/assets/brand_name.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/dashboard/web/src/assets/logo.png b/apps/dashboard/web/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/apps/dashboard/web/src/assets/logo.png differ diff --git a/apps/dashboard/web/src/assets/logo.svg b/apps/dashboard/web/src/assets/logo.svg new file mode 100644 index 0000000000..ff4fdd00d3 --- /dev/null +++ b/apps/dashboard/web/src/assets/logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/dashboard/web/src/assets/logo_facebook.svg b/apps/dashboard/web/src/assets/logo_facebook.svg new file mode 100644 index 0000000000..435a3ec21f --- /dev/null +++ b/apps/dashboard/web/src/assets/logo_facebook.svg @@ -0,0 +1 @@ + diff --git a/apps/dashboard/web/src/assets/logo_google.svg b/apps/dashboard/web/src/assets/logo_google.svg new file mode 100644 index 0000000000..f919587bcd --- /dev/null +++ b/apps/dashboard/web/src/assets/logo_google.svg @@ -0,0 +1 @@ + diff --git a/apps/dashboard/web/src/assets/logo_nav.svg b/apps/dashboard/web/src/assets/logo_nav.svg new file mode 100644 index 0000000000..d56209d245 --- /dev/null +++ b/apps/dashboard/web/src/assets/logo_nav.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/dashboard/web/src/plugins/vuetify.js b/apps/dashboard/web/src/plugins/vuetify.js new file mode 100644 index 0000000000..83251f7de8 --- /dev/null +++ b/apps/dashboard/web/src/plugins/vuetify.js @@ -0,0 +1,258 @@ +import Vue from 'vue' +import Vuetify from 'vuetify' +import 'vuetify/dist/vuetify.min.css' +import { library } from '@fortawesome/fontawesome-svg-core' // Core SVG +import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' // Integration +import CurlyBraces from "../apps/dashboard/shared/icons/CurlyBraces" +import Burpsuite from "../apps/dashboard/shared/icons/Burpsuite" +import Slack from "../apps/dashboard/shared/icons/Slack" +import GithubName from "../apps/dashboard/shared/icons/GithubName" +import GithubIcon from "../apps/dashboard/shared/icons/GithubIcon" +import Postman from "../apps/dashboard/shared/icons/Postman" +import AWS from "../apps/dashboard/shared/icons/AWS" +import GCP from "../apps/dashboard/shared/icons/GCP" +import Swagger from "../apps/dashboard/shared/icons/Swagger" +import Restapi from "../apps/dashboard/shared/icons/Restapi" +import CustomWebhooks from "../apps/dashboard/shared/icons/CustomWebhooks" +import TrashSingleTick from "../apps/dashboard/shared/icons/TrashSingleTick" +import TrashDoubleTick from "../apps/dashboard/shared/icons/TrashDoubleTick" +import AktoWhite from "../apps/dashboard/shared/icons/AktoWhite" +import BookBookmark from "../apps/dashboard/shared/icons/BookBookmark" + +Vue.component('font-awesome-icon', FontAwesomeIcon) + +import { + faClipboardList, + faLongArrowAltDown, + faLongArrowAltUp, + faUnlockAlt, + faStethoscope, + faBars, + faUser, + faPlus, + faTimes, + faEnvelope, + faPhoneAlt, + faLock, + faLockOpen, + faAngleUp, + faAngleDown, + faAngleRight, + faAngleLeft, + faComments, + faEye, + faEllipsisV, + faStar, + faCalendar, + faCog, + faShare, + faEdit, + faCheck, + faTrash, + faSearch, + faBell, + faChartBar, + faFileAlt, + faHandPointRight, + faChartArea, + faExclamationTriangle, + faInfoCircle, + faDownload, + faFileCsv, + faCircle, + faThLarge, + faStopwatch, + faCheckCircle, + faEyeSlash, + faArrowLeft, + faUsers, + faChartLine, + faHome, + faBullseye, + faBullhorn, + faCheckDouble, + faSquare, + faBolt, + faDatabase, + faAngleDoubleRight, + faAngleDoubleLeft, + faAngleDoubleDown, + faAngleDoubleUp, + faSyncAlt, + faExchangeAlt, + faUserLock, + faExclamation, + faCreditCard, + faAddressCard, + faChalkboard, + faFilter, + faKey, + faUpload, + faRedo, + faGlobe, + faUserSecret, + faCopy, + faPlay, + faStop, + faPen, + faCalendarPlus, + faCalendarTimes, + faCaretSquareUp, + faThumbsUp, + faCaretUp, + faCaretDown, + faShareAlt, + faUserPlus, + faLaptopHouse, + faAnchor, + faArrowRight, + faGift +} from '@fortawesome/free-solid-svg-icons' + +import { + faComments as farComments, + faCalendar as farCalendar, + faHandPointRight as farHandPointRight, + faBuilding as farBuilding, + faStar as farStar, + faSquare as farSquare, + faCheckSquare as farCheckSquare, + faFolder as farFolder, + faFolderOpen as farFolderOpen, + faClock as farClock, + faQuestionCircle as farQuestionCircle, +} from '@fortawesome/free-regular-svg-icons' + +import { + faSalesforce as fabSalesforce, + faHubspot as fabHubspot, + faGithub as fabGithub +} from '@fortawesome/free-brands-svg-icons' + +const faIcons = [ + faBars,faUser,faPlus,faTimes,faEnvelope,faLock,faLockOpen,faAngleUp,faAngleRight,faAngleLeft,faComments,faEye,faEllipsisV,faStar,faCalendar, + faCog,faShare,faEdit,faCheck,faTrash,faSearch,faBell,faChartBar,faFileAlt,faHandPointRight,faChartArea,faExclamationTriangle, + faInfoCircle,faDownload,faFileCsv,faCircle,faThLarge,faStopwatch,faCheckCircle,faEyeSlash,faArrowLeft,faUsers,faChartLine, + faHome,faBullseye,faBullhorn,faCheckDouble,faSquare,faBolt,faDatabase,faAngleDoubleRight, faAngleDoubleLeft,faClipboardList, + faUnlockAlt,faStethoscope,faLongArrowAltDown,faLongArrowAltUp,faSyncAlt, faExchangeAlt, faUserLock, faExclamation,faPhoneAlt, + faCreditCard,faAddressCard,faChalkboard,faFilter,faKey,faAngleDoubleDown,faAngleDoubleUp,faUpload,faRedo,faGlobe,faAngleDown, + faUserSecret,faCopy,faPlay,faStop,faPen,faCalendarPlus,faCalendarTimes,faCaretSquareUp, + faUserSecret,faCopy,faPlay,faStop,faPen,faCalendarPlus,faCalendarTimes,faThumbsUp,faCaretUp,faShareAlt,faUserPlus,faCaretDown, + faLaptopHouse,faAnchor,fabGithub,faArrowRight,faGift, + + farComments,farCalendar, farHandPointRight, farBuilding, farStar,farSquare, farCheckSquare,farFolder,farFolderOpen,farClock, + farQuestionCircle, + + fabSalesforce,fabHubspot +] + +library.add(faIcons) + +Vue.use(Vuetify) + +const ctTheme = { + primary: '#126BFF', + secondary: '#434761', + secondaryBtn: '#4900AF', + errorBtn: '#FF5353', + negativeBtn: '#63698f', + toggleBtn: '#ECEDF2', + accent: '#2d2434', + error: '#FF5252', + info: '#2196F3', + success: '#4CAF50', + warning: '#FFC107', + base: '#2d2434', + darken1: '#63698F', + darken2: '#434761', + lighten1: '#D0D2E1', + anchor: '#085ce7', + redMetric: '#f44336', + greenMetric: '#00bfa5', + themeColor: '#6200EA' +} + +const faIconsOpts = {} + +faIconsOpts.bookBookmark = { + component: BookBookmark +} + +faIconsOpts.aktoWhite = { + component: AktoWhite +} + +faIconsOpts.curlyBraces = { + component: CurlyBraces +} + +faIconsOpts.burpsuite = { + component: Burpsuite +} + +faIconsOpts.postman = { + component: Postman +} + +faIconsOpts.aws = { + component: AWS +} + +faIconsOpts.swagger = { + component: Swagger +} + +faIconsOpts.gcp = { + component: GCP +} + +faIconsOpts.slack = { + component: Slack +} + +faIconsOpts.restapi = { + component: Restapi +} + +faIconsOpts.githubName = { + component: GithubName +} + +faIconsOpts.githubIcon = { + component: GithubIcon +} + +faIconsOpts.customwebhooks = { + component: CustomWebhooks +} + +faIconsOpts.trashSingleTick = { + component: TrashSingleTick +} + +faIconsOpts.trashDoubleTick = { + component: TrashDoubleTick +} + +faIcons.forEach(x => faIconsOpts[x.prefix+"_"+x.iconName] = { + component: FontAwesomeIcon, + props: { + icon: [x.prefix, x.iconName] + } +}) + +const opts = { + theme: { + themes: { + light: ctTheme, + dark: ctTheme + }, + options: { customProperties: true }, + }, + icons: { + values: faIconsOpts + } +} + +export default new Vuetify(opts) + diff --git a/apps/dashboard/web/src/theme/default.sass b/apps/dashboard/web/src/theme/default.sass new file mode 100644 index 0000000000..46b8ec56b6 --- /dev/null +++ b/apps/dashboard/web/src/theme/default.sass @@ -0,0 +1,47 @@ +/* ------------------------------------------------------------------ + * [Table of contents] + */ +// # overwrite some style of SASS + +/*------------------------------------------------------------------ + * [FontAwesome] + */ + +/*------------------------------------------------------------------ + * [vuetify] + */ +$color-pack: true +@import '~vuetify/src/styles/main.sass' + + + + +/*------------------------------------------------------------------ + * [Components / Chat] + */ +.chat + &-history + &--toolbar + box-shadow: 0 1px 3px 1px rgba(0,0,0,0.03) + &--list + a.list__tile--active + background: #eee + &--scrollbar + height: calc(100vh - 48px) + @media #{map-get($display-breakpoints, 'sm-and-down')} + height: calc(100vh - 48px - 56px) + &-room + &--toolbar + box-shadow: 0 1px 3px 1px rgba(0,0,0,0.03) + &--scrollbar + height: calc(100vh - 48px - 80px) + +.v-input.search + .v-input__slot + margin-top: 8px +/*------------------------------------------------------------------ + * [Components / AppToolbar] + */ +.app--toolbar + .v-toolbar__extension + padding: 0 !important \ No newline at end of file diff --git a/apps/dashboard/web/src/util/constants.js b/apps/dashboard/web/src/util/constants.js new file mode 100644 index 0000000000..2426b87e67 --- /dev/null +++ b/apps/dashboard/web/src/util/constants.js @@ -0,0 +1,5 @@ +export default { + + DISCOVERED: 'Discovered' + +} \ No newline at end of file diff --git a/apps/dashboard/web/src/util/func.js b/apps/dashboard/web/src/util/func.js new file mode 100644 index 0000000000..38994a0474 --- /dev/null +++ b/apps/dashboard/web/src/util/func.js @@ -0,0 +1,662 @@ +import vuetify from "../plugins/vuetify" +export default { + trackingPeriodStr: (num) => { + switch (num) { + case 0: return 'daily' + case 1: return 'weekly' + case 2: return 'monthly' + case 3: return 'quarterly' + case 4: return 'half-yearly' + case 5: return 'yearly' + } + }, + trackingPeriodNum: (str) => { + switch (str) { + case 'daily': return 0 + case 'weekly': return 1 + case 'monthly': return 2 + case 'quarterly': return 3 + case 'half-yearly': return 4 + case 'yearly': return 5 + } + }, + sourceNum: (str) => { + switch (str.toLowerCase().replaceAll(" ", "")) { + case 'spreadsheet': return 0 + case 'googlesheets': return 0 + case 'salesforce': return 1 + case 'hubspot': return 2 + case 'mysql': return 3 + case 'postgresql': return 4 + case 'redshift': return 5 + case 'bigtable': return 6 + case 'snowflake': return 7 + + } + }, + returnResultTypes: () => { + return ['NUMERIC', 'AVERAGE'] + }, + sourceStr: (num) => { + switch (num) { + case 0: return 'Spreadsheet' + case 1: return 'Salesforce' + case 2: return 'Hubspot' + } + }, + icon: (num) => { + switch (num) { + case 0: return {name: '$fas_file-csv', color: '#33a852'} + case 1: return {name: '$fab_salesforce', color: '#17A0DB'} + case 2: return {name: '$fab_hubspot', color: '#fe7b5b'} + case 3: return {name: '$fas_database', color: '#47466A'} + case 4: return {name: '$fas_database', color: '#47466A'} + case 5: return {name: '$fas_database', color: '#47466A'} + case 6: return {name: '$fas_database', color: '#47466A'} + case 7: return {name: '$fas_database', color: '#47466A'} + } + }, + + pretty: (num) => { + return (Math.round(num * 100)/100).toLocaleString() + }, + timeNow: () => { + return parseInt(new Date().getTime()/1000) + }, + todayDate: () => { + return new Date(Date.now()) + }, + epochToDateTime: (timestamp) => { + var date = new Date(timestamp * 1000); + + var year = date.getFullYear(); + var month = date.getMonth() + 1; + var day = date.getDate(); + var hours = date.getHours(); + var minutes = date.getMinutes(); + var seconds = date.getSeconds(); + + return year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds + }, + dayStart(epochMs) { + let date = new Date(epochMs) + date.setHours(0) + date.setMinutes(0) + date.setSeconds(0) + return date + }, + dayEnd(epochMs) { + let date = new Date(epochMs) + date.setHours(23) + date.setMinutes(59) + date.setSeconds(59) + return date + }, + weekStart (date) { + let date1 = new Date(date.getTime()) + return new Date(date1.setDate(date1.getDate() - date1.getDay() + (date1.getDay() === 0 ? -6 : 1))) + }, + weekEnd (date) { + let date1 = this.weekStart(date) + return new Date(date1.setDate(date1.getDate() + 6)) + }, + monthStart (date) { + return new Date(date.getFullYear(),date.getMonth(),1) + }, + monthEnd (date) { + return new Date(date.getFullYear(),date.getMonth()+1,0) // 0 gives last date of prev month + }, + quarterStart(date) { + return new Date(date.getFullYear(),Math.floor(date.getMonth() / 3) * 3,1) + }, + quarterEnd(date) { + let customDate = this.quarterStart(new Date(date)) + customDate.setMonth(customDate.getMonth()+3) + customDate.setDate(0) + return customDate + + }, + days_between(date1,date2) { + const oneDay = 24 * 60 * 60 * 1000; + let firstDate = new Date(date1) + firstDate.setHours(0,0,0) + let secondDate = new Date(date2) + secondDate.setHours(0,0,0) + return Math.round(Math.abs((firstDate - secondDate) / oneDay)) + 1 + }, + toDate (yyyymmdd) { + return +new Date(yyyymmdd/10000, (yyyymmdd/100)%100 - 1, yyyymmdd%100) + }, + toHyphenated (yyyymmdd) { + let month = parseInt(yyyymmdd/100)%100 + month = (month < 10 ? "0" : "")+month + + let day = parseInt(yyyymmdd)%100 + day = (day < 10 ? "0" : "")+day + return [parseInt(yyyymmdd/10000), month, day].join("-") + }, + toDateStr (date, needYear) { + var strArray=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + var d = date.getDate(); + var m = strArray[date.getMonth()]; + var y = date.getFullYear(); + return m + ' ' + d + (needYear ? ' ' + y: '' ); + }, + toTimeStr (date, needYear) { + var strArray=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + var d = date.getDate(); + var m = strArray[date.getMonth()]; + var y = date.getFullYear(); + return m + ' ' + d + ', ' + (needYear ? y: '' ) + ' ' + date.toISOString().substr(11,5) + }, + toDateStrShort(date) { + var d = "" + date.getDate(); + var m = "" + (date.getMonth()+1); + var y = "" + date.getFullYear(); + if (m.length < 2) m = '0' + m + if (d.length < 2) d = '0' + d + return y + "-" + m + "-" + d + }, + toYMD (date) { + var d = date.getDate(); + var m = date.getMonth() + 1; //Month from 0 to 11 + var y = date.getFullYear(); + return y * 10000 + m * 100 + d + }, + prettifyShort(num) { + return new Intl.NumberFormat( 'en-US', { maximumFractionDigits: 1,notation: "compact" , compactDisplay: "short" }).format(num) + }, + prettifyEpoch(epoch) { + var diffSeconds = (+Date.now())/1000 - epoch + if (diffSeconds < 120) { + return '1 minute ago' + } else if (diffSeconds < 3600) { + return Math.round(diffSeconds/60) + ' minutes ago' + } else if (diffSeconds < 7200) { + return '1 hour ago' + } else if (diffSeconds < 86400) { + return Math.round(diffSeconds/3600) + ' hours ago' + } + + var diffDays = diffSeconds/86400 + + var diffWeeks = diffDays/7 + + var diffMonths = diffDays/30 + + var count = Math.round(diffDays) + var unit = 'day' + + if (diffMonths > 2) { + return this.toDateStr(new Date(epoch*1000), true) + } else if (diffWeeks > 4) { + count = Math.round(diffMonths + 0.5) + unit = 'month' + } else if (diffDays > 11) { + count = Math.round(diffWeeks + 0.5) + unit = 'week' + } else if (diffDays == 1) { + return sign > 0 ? 'tomorrow' : 'yesterday' + } else if (diffDays == 0) { + return 'today' + } + + var plural = count <= 1 ? '' : 's' + + return count + ' ' + unit + plural + ' ago' + }, + getCreationTime(creationTime) { + let epoch = Date.now(); + let difference = epoch / 1000 - creationTime; + let numberOfDays = difference / (60 * 60 * 24) + if (numberOfDays < 31) { + return parseInt(numberOfDays) + ' d'; + } else if (numberOfDays < 366) { + return parseInt(numberOfDays / 31) + ' m' + parseInt(numberOfDays % 31) + ' d'; + } else { + return parseInt(numberOfDays / 365) + ' y' + parseInt((numberOfDays % 365) / 31) + ' m' + + parseInt((numberOfDays % 365) % 31) + ' d'; + } + }, + prettifyDate(date) { + var diffSeconds = Math.abs(this.toDate(date) - this.toDate(this.toYMD(new Date(Date.now()))))/1000 + + + var diffDays = diffSeconds/86400 + var sign = this.toDate(date) > Date.now() + + var diffWeeks = diffDays/7 + + var diffMonths = diffDays/30 + + var count = diffDays + var unit = 'day' + + if (diffMonths > 2) { + return this.toDateStr(new Date(this.toDate(date)), true) + } else if (diffWeeks > 4) { + count = Math.round(diffMonths + 0.5) + unit = 'month' + } else if (diffDays > 11) { + count = Math.round(diffWeeks + 0.5) + unit = 'week' + } else if (diffDays == 1) { + return sign > 0 ? 'tomorrow' : 'yesterday' + } else if (diffDays == 0) { + return 'today' + } + + var plural = count <= 1 ? '' : 's' + + if (sign > 0) { + return 'in ' + count + ' ' + unit + plural + } else { + return count + ' ' + unit + plural + ' ago' + } + + }, + getActionItems(list, colName) { + if (!list || list.length == 0) { + return [] + } + + switch (colName.replaceAll(' ','').toLowerCase()) { + case "thisweek": + return list.filter(x => x.current_status == 0 && x.dueDate <= +(this.weekEnd(this.todayDate())/1000.0) && x.dueDate > this.timeNow()) + case "pending": + return list.filter(x => x.current_status == 0 && x.dueDate > +(this.weekEnd(this.todayDate())/1000.0)) + case "overdue": + case "pastduedate": + return list.filter(x => x.current_status == 0 && x.dueDate <= this.timeNow()) + case "completed": + return list.filter(x => x.current_status != 0) + case "total": + return list + } + }, + initials(str) { + var ret = str.split(" ").map((n)=>n[0]).slice(0,2).join("").toUpperCase() + + if (ret.length == 1) { + return str.replaceAll(" ","").slice(0,2).toUpperCase() + } else { + return ret + } + }, + hashCode(str) { + var hash = 0; + for (var i = 0; i < str.length; i++) { + var character = str.charCodeAt(i); + hash = ((hash<<5)-hash)+character; + hash = hash & hash; // Convert to 32bit integer + } + return Math.abs(hash); + }, + colors() { + return ['red', 'blue', 'green', 'purple', 'brown'] + }, + chartColors() { + return ['#1EE0B7', '#FF4DCA','#167414','#F68B4F', '#41B3E9'] + }, + convertToPairs(trend) { + return Object.entries(trend).map(x => { + return [this.toDate(x[0]), Math.round(100*(+x[1]))/100] + }) + }, + actionItemColors() { + return { + Total: 'rgba(71, 70, 106)', + Pending: 'rgba(246, 190, 79)', + Overdue: 'rgba(243, 107, 107)', + 'This week': 'rgba(33, 150, 243)', + Completed: 'rgba(0, 191, 165)' + }; + }, + incrDays(date, days) { + var ret = new Date(date.getFullYear(), date.getMonth(), date.getDate()) + ret.setDate(ret.getDate() + days) + return ret + }, + incrMonths(date, months) { + var ret = new Date(date.getFullYear(), date.getMonth(), date.getDate()) + ret.setMonth(ret.getMonth() + months) + return ret + }, + calculateMetricTrend(mapDateToDatapoint, getKey, cumulative) { + mapDateToDatapoint = Object.entries(mapDateToDatapoint).reduce( (z, e) => { + let key = getKey(e[0]) + z[key] = z[key] || 0 + z[key] += e[1].value.number + return z + }, {}) + + let newSeries = [...Object.entries(mapDateToDatapoint)].sort((a,b) => { + return +a[0] - +b[0] + }) + + let ret = [] + newSeries.forEach((x, i) => { + ret.push([this.toDate(+x[0]), Math.round((x[1] + (i > 0 && cumulative ? ret[i-1][1] : 0)) * 100)/100]) + }) + + return ret + }, + isSubTypeSensitive(x) { + return x.savedAsSensitive || x.sensitive + }, + parameterizeUrl(x) { + let re = /INTEGER|STRING/gi; + let newStr = x.replace(re, (match) => { + return "{param_" + match + "}"; + }); + return newStr + }, + mergeApiInfoAndApiCollection(listEndpoints, apiInfoList, idToName) { + let ret = {} + let apiInfoMap = {} + + if (!listEndpoints) { + return [] + } + + if (apiInfoList) { + apiInfoList.forEach(x => { + apiInfoMap[x["id"]["apiCollectionId"] + "-" + x["id"]["url"] + "-" + x["id"]["method"]] = x + }) + } + + listEndpoints.forEach(x => { + let key = x.apiCollectionId + "-" + x.url + "-" + x.method + if (!ret[key]) { + let access_type = null + if (apiInfoMap[key]) { + let access_types = apiInfoMap[key]["apiAccessTypes"] + if (!access_types || access_types.length == 0) { + access_type = null + } else if (access_types.indexOf("PUBLIC") !== -1) { + access_type = "Public" + } else { + access_type = "Private" + } + } + + let authType = apiInfoMap[key] ? apiInfoMap[key]["actualAuthType"].join(" or ") : "" + + ret[key] = { + shadow: x.shadow ? x.shadow : false, + sensitive: x.sensitive, + tags: x.tags, + endpoint: x.url, + parameterisedEndpoint: this.parameterizeUrl(x.url), + open: apiInfoMap[key] ? apiInfoMap[key]["actualAuthType"].indexOf("UNAUTHENTICATED") !== -1 : false, + access_type: access_type, + method: x.method, + color: x.sensitive && x.sensitive.size > 0 ? "#f44336" : "#00bfa5", + apiCollectionId: x.apiCollectionId, + last_seen: apiInfoMap[key] ? this.prettifyEpoch(apiInfoMap[key]["lastSeen"]) : 0, + detectedTs: x.startTs, + changesCount: x.changesCount, + changes: x.changesCount && x.changesCount > 0 ? (x.changesCount +" new parameter"+(x.changesCount > 1? "s": "")) : '-', + added: this.prettifyEpoch(x.startTs), + violations: apiInfoMap[key] ? apiInfoMap[key]["violations"] : {}, + apiCollectionName: idToName ? (idToName[x.apiCollectionId] || '-') : '-', + auth_type: (authType || "").toLowerCase(), + sensitiveTags: this.convertSensitiveTags(x.sensitive) + } + + } + }) + + return Object.values(ret) + }, + + convertSensitiveTags(subTypeList) { + let result = new Set() + if (!subTypeList || subTypeList.size === 0) return result + + subTypeList.forEach((x) => { + result.add(x.name) + }) + + return result + }, + + recencyPeriod: 60 * 24 * 60 * 60, + toCommaSeparatedNumber(number) { + let nf = new Intl.NumberFormat('en-US'); + return nf.format(number); + }, + sensitiveTagDetails(tag) { + let icon = "$fas_info-circle" + switch(tag) { + case "EMAIL": + icon = "$fas_envelope" + break; + case "PHONE_NUMBER_INDIA": + case "PHONE_NUMBER_US": + case "PHONE_NUMBER": + icon = "$fas_phone-alt" + break; + case "CREDIT_CARD": + icon = "$fas_credit-card" + break; + case "SSN": + icon = "$fas_chalkboard" + break; + case "PAN_CARD": + icon = "$fas_address-card" + break; + case "JWT": + icon = "$fas_key" + break; + case "IP_ADDRESS": + icon = "$fas_globe" + break; + default: + break; + } + return icon + }, + + preparePredicatesForApi(predicates) { + let result = [] + if (!predicates) return result + predicates.forEach((predicate) => { + let type = predicate["type"] + let obj = {"type": type} + + let valueMap = {} + switch (type) { + case "STARTS_WITH": + case "ENDS_WITH": + case "REGEX": + case "EQUALS_TO": + valueMap["value"] = predicate["value"] + break; + + case "IS_NUMBER": + break; + + } + + obj["valueMap"] = valueMap + + result.push(obj) + }) + + return result; + }, + + generateKeysForApi(predicates){ + let result =[] + if(!predicates) return result + predicates.forEach((predicate)=>{ + result.push(predicate["value"]) + }) + return result; + }, + + prepareAuthTypes(auth_types){ + if(auth_types) { + auth_types.forEach((x)=>{ + x["operator"]="OR" + x["prefix"] = x["id"] ? "[custom]" : "" + let headerPredicates =[] + x["headerKeys"].forEach((key)=>{ + let obj = {"type":"EQUALS_TO","value":key} + headerPredicates.push(obj) + }) + let payloadPredicates =[] + x["payloadKeys"].forEach((key)=>{ + let obj = {"type":"EQUALS_TO","value":key} + payloadPredicates.push(obj) + }) + if(x["id"]){ + x["headerKeyConditions"] = { + "operator":"AND", + "predicates": headerPredicates + } + x["payloadKeyConditions"] = { + "operator":"AND", + "predicates": payloadPredicates + } + } + }) + } + return auth_types + }, + + prepareDataTypes(data_types) { + if (data_types) { + data_types.forEach((x) => { + let isSensitive = x["sensitiveAlways"] || (x["sensitivePosition"] && x["sensitivePosition"].length > 0) + x["color"] = isSensitive ? vuetify.userPreset.theme.themes.dark.redMetric : "transparent" + x["prefix"] = x["id"] ? "[custom]" : "" + if (x["id"]) { + if (!x["keyConditions"]) { + x["keyConditions"] = {"operator": "AND", "predicates": []} + } + if (!x["valueConditions"]) { + x["valueConditions"] = {"operator": "AND", "predicates": []} + } + } else { + x["active"] = true + } + }) + + data_types.sort(function(a,b){ + if (a["id"] && b["id"]) return b["id"]["timestamp"] - a["id"]["timestamp"] + return a["id"] ? -1 : 1 + }) + + data_types.sort(function(a,b){ + if (a["active"] && b["active"]) return 0 + return a["active"] ? -1 : 1 + }) + } + }, + + prettifySensitivePosition(sensitivePosition) { + let andIndex = sensitivePosition.length - 2 + let result = "" + for (let idx=0; idx < sensitivePosition.length; idx++) { + result += sensitivePosition[idx] + if (andIndex) { + result += "and" + } else { + result += "," + } + } + + return result + }, + + icon(name) { + switch (name) { + case "BURPSUITE": + return {name: '$burpsuite', color: '#fe7b5b'} + + case "POSTMAN": + return {name: '$postman', color: '#ffffff'} + + case "AKTOAPI": + return {name: '$restapi', color: '#fe7b5b'} + + case "SLACK": + return {name: '$slack', color: '#fe7b5b'} + + case "WEBHOOKS": + return {name: '$customwebhooks', color: '#fe7b5b'} + } + }, + + prepareDomain(x) { + let NO_VALUES_RECORDED = "-"; + if (x.domain === "RANGE") { + return x.minValue + " - " + x.maxValue + } else if (x.domain === "ANY") { + return "ANY" + } else { + let values = x["values"] + if (!values) return NO_VALUES_RECORDED + + let elements = values["elements"] + if (!elements) return NO_VALUES_RECORDED + + let size = elements.length + + if (size === 0) { + return NO_VALUES_RECORDED + } + + let count = 0 + let result = "" + const limit = 2 + for (var elem of elements) { + if (count >= limit) { + result += " and " + (size - limit) + " more" + return result + } + + if (count !== 0) { + result += ", " + } + + result += elem + count += 1 + + } + + return result; + + } + }, + + prepareValuesTooltip(x) { + let result = ""; + let values = x["values"] + if (!values) return "No values recorded" + let elements = values["elements"] ? values["elements"] : [] + let count = 0; + for (var elem of elements) { + if (count > 50) return result + if (count !== 0) { + result += ", " + } + result += elem + count += 1 + } + + return (count == 0 ? "No values recorded" : result) + }, + + showErrorSnackBar(val){ + window._AKTO.$emit('SHOW_SNACKBAR', {show: true, text: val, color: 'red'}) + }, + showSuccessSnackBar(val){ + window._AKTO.$emit('SHOW_SNACKBAR', {show: true, text: val, color: 'green'}) + }, + toEpochInMs(hyphenatedDate) { + return +this.toDate(hyphenatedDate.replace(/\-/g, '')) + }, +} \ No newline at end of file diff --git a/apps/dashboard/web/src/util/merge.js b/apps/dashboard/web/src/util/merge.js new file mode 100644 index 0000000000..f3d5dcfff7 --- /dev/null +++ b/apps/dashboard/web/src/util/merge.js @@ -0,0 +1,74 @@ + function isMergeableObject(val) { + var nonNullObject = val && typeof val === 'object' + + return nonNullObject + && Object.prototype.toString.call(val) !== '[object RegExp]' + && Object.prototype.toString.call(val) !== '[object Date]' + } + + function emptyTarget(val) { + return Array.isArray(val) ? [] : {} + } + + function cloneIfNecessary(value, optionsArgument) { + var clone = optionsArgument && optionsArgument.clone === true + return (clone && isMergeableObject(value)) ? deepmerge(emptyTarget(value), value, optionsArgument) : value + } + + function defaultArrayMerge(target, source, optionsArgument) { + var destination = target.slice() + source.forEach(function (e, i) { + if (typeof destination[i] === 'undefined') { + destination[i] = cloneIfNecessary(e, optionsArgument) + } else if (isMergeableObject(e)) { + destination[i] = deepmerge(target[i], e, optionsArgument) + } else if (target.indexOf(e) === -1) { + destination.push(cloneIfNecessary(e, optionsArgument)) + } + }) + return destination + } + +export default { + + mergeObject: function(target, source, optionsArgument) { + var destination = {} + if (isMergeableObject(target)) { + Object.keys(target).forEach(function (key) { + destination[key] = cloneIfNecessary(target[key], optionsArgument) + }) + } + var obj = this + Object.keys(source).forEach(function (key) { + if (!isMergeableObject(source[key]) || !target[key]) { + destination[key] = cloneIfNecessary(source[key], optionsArgument) + } else { + destination[key] = obj.deepmerge(target[key], source[key], optionsArgument) + } + }) + return destination + }, + + deepmerge: function(target, source, optionsArgument) { + var array = Array.isArray(source); + var options = optionsArgument || {arrayMerge: defaultArrayMerge} + var arrayMerge = options.arrayMerge || defaultArrayMerge + + if (array) { + return Array.isArray(target) ? arrayMerge(target, source, optionsArgument) : cloneIfNecessary(source, optionsArgument) + } else { + return this.mergeObject(target, source, optionsArgument) + } + }, + + deepmergeAll: function(array, optionsArgument) { + if (!Array.isArray(array) || array.length < 2) { + throw new Error('first argument should be an array with at least two elements') + } + + // we are sure there are at least 2 values, so it is safe to have no initial value + return array.reduce(function (prev, next) { + return this.deepmerge(prev, next, optionsArgument) + }) + } +} diff --git a/apps/dashboard/web/src/util/notifications.js b/apps/dashboard/web/src/util/notifications.js new file mode 100644 index 0000000000..40f85cf163 --- /dev/null +++ b/apps/dashboard/web/src/util/notifications.js @@ -0,0 +1,52 @@ +// urlB64ToUint8Array is a magic function that will encode the base64 public key +// to Array buffer which is needed by the subscription option + +import request from "@/util/request"; + +const urlB64ToUint8Array = base64String => { + const padding = '='.repeat((4 - (base64String.length % 4)) % 4) + const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/') + const rawData = atob(base64) + const outputArray = new Uint8Array(rawData.length) + for (let i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i) + } + return outputArray +} + +const registerServiceWorker = async () => { + const swRegistration = await navigator.serviceWorker.register('/sw.js'); //notice the file name + return swRegistration; +} + +const requestNotificationPermission = async () => { + const permission = await window.Notification.requestPermission(); + if(permission !== 'granted'){ + throw new Error('Permission not granted for Notification'); + } +} + +export default { + triggerSubscriptionAlert: async () => { //notice I changed main to async function so that I can use await for registerServiceWorker + const swRegistration = await registerServiceWorker(); + await requestNotificationPermission(); + if (window.Notification.permission === 'granted') { + const applicationServerKey = urlB64ToUint8Array( + 'BMjkoJ-L23wEQ-IcPtGAMAo7tLK7YYMX69rte8h8E_2bLNHT1fVH4YabKxQtQNDmMBjjXf_nlFPuCotfpZscaBU' + ) + const options = { applicationServerKey, userVisibleOnly: true } + swRegistration.pushManager.subscribe(options).then(subscription => { + var subscriptionData = JSON.parse(JSON.stringify(subscription)); + subscriptionData.userAgent = navigator.userAgent; + + request({ + url: '/api/saveSubscription', + method: 'post', + data: {subscription} + }).then((resp)=>{ + return resp + }) + }) + } + } +} diff --git a/apps/dashboard/web/src/util/obj.js b/apps/dashboard/web/src/util/obj.js new file mode 100644 index 0000000000..e982355bab --- /dev/null +++ b/apps/dashboard/web/src/util/obj.js @@ -0,0 +1,50 @@ +export default { + numR: { + type: Number, + required: true + }, + strR: { + type: String, + required: true + }, + arrR: { + type: Array, + required: true + }, + objR: { + type: Object, + required: true + }, + boolR: { + type: Boolean, + required: true + }, + + + numN: { + type: Number, + required: false + }, + objN: { + type: Object, + required: false + }, + strN: { + type: String, + required: false + }, + arrN: { + type: Array, + required: false + }, + boolN: { + type: Boolean, + required: false + }, + + overviewChartOpts: { + legend: { + enabled: false + } + } +} \ No newline at end of file diff --git a/apps/dashboard/web/src/util/request.js b/apps/dashboard/web/src/util/request.js new file mode 100644 index 0000000000..9c544ff6b1 --- /dev/null +++ b/apps/dashboard/web/src/util/request.js @@ -0,0 +1,142 @@ +import axios from 'axios' +import store from "@/apps/main/store/module"; +import router from "@/apps/main/router"; + +// create axios +const service = axios.create({ + baseURL: window.location.origin, // api base_url + timeout: 50000, // timeout, + headers: { 'Access-Control-Allow-Origin': '*' } +}) + +const err = async (error) => { + const { status, data } = error.response + const { errors } = data + const { actionErrors } = data + let message = "OOPS! Something went wrong" + if (actionErrors !== null && actionErrors !== undefined && actionErrors.length > 0) { + message = actionErrors[0] + } + switch (status) { + case 400: + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: 'Bad Request ' + data.message, + color: 'red' + }) + break + + case 422: + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: message, + color: 'red' + }) + + break + + case 401: + if (router.currentRoute.path !== "/login") { + router.push('/login').then(() => { + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Please login again", + color: 'red' + }); + } + ) + } + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Please login again", + color: 'red' + }) + break + + case 423: + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Confirm your email first", + color: 'red' + }) + break + + case 429: + window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Too many requests!! Please try after 1 hour", + color: 'red' + }) + break + + case 403: + const originalRequest = error.config; + if (originalRequest._retry) { + await window._AKTO.$emit('SHOW_SNACKBAR', { + show: true, + text: "Something went wrong", + color: 'red' + }) + break + } + originalRequest._retry = true + await service({ + url: '/dashboard/accessToken', + method: 'get', + }) + return service(originalRequest) + + case 500: + window._AKTO.$emit('SERVER_ERROR') + break + + default: + break + } + + return Promise.reject(error) +} + +// request interceptor +// For every request that is sent from the vue app, automatically attach the accessToken from the store +service.interceptors.request.use((config) => { + config.headers['Access-Control-Allow-Origin'] = '*' + config.headers['Content-Type'] = 'application/json' + config.headers["access-token"] = store.getters["auth/getAccessToken"] + + if (window.ACTIVE_ACCOUNT) { + config.headers['account'] = window.ACTIVE_ACCOUNT + } + + return config +}, err) + +// response interceptor +// For every response that is sent to the vue app, look for access token in header and set it if not null +service.interceptors.response.use((response) => { + if (response.headers["access-token"] != null) { + store.commit('auth/SET_ACCESS_TOKEN',response.headers["access-token"]) + } + + if (['put', 'post', 'delete', 'patch'].includes(response.method) && response.data.meta) { + window._AKTO.$emit('SHOW_SNACKBAR', { + text: response.data.meta.message, + color: 'success' + }) + } + if (response.data.error !== undefined) { + window._AKTO.$emit('API_FAILED', response.data.error) + } else { + if (window.mixpanel && window.mixpanel.track && response.config && response.config.url){ + raiseMixpanelEvent(response.config.url); + } + } + + return response.data +}, err) + +async function raiseMixpanelEvent(api){ + window.mixpanel.track(api) +} + +export default service diff --git a/apps/dashboard/web/src/util/testing.js b/apps/dashboard/web/src/util/testing.js new file mode 100644 index 0000000000..c5bb7f96cd --- /dev/null +++ b/apps/dashboard/web/src/util/testing.js @@ -0,0 +1,28 @@ +export default { + getCollectionName: (testingEndpoints, mapCollectionIdToName) => { + let apiCollectionId = 0 + switch (testingEndpoints.type) { + case "CUSTOM": + apiCollectionId = testingEndpoints.apisList[0].apiCollectionId + break; + case "COLLECTION_WISE": + apiCollectionId = testingEndpoints.apiCollectionId + break; + case "WORKFLOW": + apiCollectionId = testingEndpoints.workflowTest.apiCollectionId + break; + } + + return mapCollectionIdToName[apiCollectionId] + }, + getEndpoints: (testingEndpoints) => { + switch (testingEndpoints.type) { + case "CUSTOM": + return testingEndpoints.apisList.length + case "COLLECTION_WISE": + return "all" + case "WORKFLOW": + return "-" + } + } +} \ No newline at end of file diff --git a/apps/dashboard/web/sw.js b/apps/dashboard/web/sw.js new file mode 100644 index 0000000000..2de142ff73 --- /dev/null +++ b/apps/dashboard/web/sw.js @@ -0,0 +1,19 @@ +console.log('service worker msgs') + +self.addEventListener('fetch', function(event) { + // console.log(event.request.url); +}); + +self.addEventListener('push', function(event) { + if (event.data) { + console.log('Push event!! ', event.data.text()) + self.registration.showNotification('Akto', JSON.parse(event.data.text())) + } else { + console.log('Push event but no data') + } +}) + +self.addEventListener('activate', async () => { + // This will be called only once when the service worker is activated. + console.log('service worker activate') +}) \ No newline at end of file diff --git a/apps/dashboard/web/webpack.config.js b/apps/dashboard/web/webpack.config.js new file mode 100644 index 0000000000..63774a84a8 --- /dev/null +++ b/apps/dashboard/web/webpack.config.js @@ -0,0 +1,161 @@ +var path = require('path') +var webpack = require('webpack') +require("babel-polyfill") +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') +const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin') + + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +function hashGenerator() { + return 1 +} + +process.env.HASH = hashGenerator() + +module.exports = { + entry: {"babel-polyfill": "babel-polyfill", main: './web/src/apps/main/main.js', chrome_plugin: './web/src/apps/chrome_plugin/main.js'}, + output: { + path: path.resolve(__dirname, './dist'), + publicPath: '/dist/', + filename: '[name].js' + }, + module: { + rules: [ + { + test: /\.css$/, + use: [ + 'vue-style-loader', + 'css-loader' + ], + }, + { + test: /\.scss$/, + use: [ + 'vue-style-loader', + 'css-loader', + 'sass-loader' + ], + }, + { + test: /\.sass$/, + use: [ + 'vue-style-loader', + 'css-loader', + { + loader: 'sass-loader', + options: { + indentedSyntax: true, + // sass-loader version >= 8 + sassOptions: { + indentedSyntax: true + } + } + } + ] + }, + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + loaders: { + // Since sass-loader (weirdly) has SCSS as its default parse mode, we map + // the "scss" and "sass" values for the lang attribute to the right configs here. + // other preprocessors should work out of the box, no loader config like this necessary. + 'scss': [ + 'vue-style-loader', + 'css-loader', + { + loader: 'sass-loader', + options: { + // sass-loader version >= 8 + sassOptions: { + indentedSyntax: true + } + } + } + ], + 'sass': [ + 'vue-style-loader', + 'css-loader', + 'sass-loader?indentedSyntax' + ] + } + // other vue-loader options go here + } + }, + { + test: /\.(js|jsx)$/, + exclude: /node_modules\/(?!(tiptap|tiptap-utils|are-you-es5|tiptap-extensions)\/)/, + use: { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env'] + } + } + + }, + { + test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/, + loader: 'file-loader', + options: { + name: 'images/[name].[ext]?[hash]' + } + } + ] + }, + resolve: { + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('/web/src') + }, + extensions: ['*', '.js', '.vue', '.json'] + }, + devServer: { + historyApiFallback: true, + noInfo: true, + overlay: true + }, + performance: { + hints: false + }, + devtool: '#eval-source-map' +} + +if (process.env.NODE_ENV === 'production') { + module.exports.devtool = '#source-map' + // http://vue-loader.vuejs.org/en/workflow/production.html + module.exports.plugins = (module.exports.plugins || []).concat([ + new SWPrecacheWebpackPlugin({ + cacheId: 'akto-app', + filename: 'sw.js', + staticFileGlobs: ['dist/**/*.{js,css}', '/'], + minify: true, + stripPrefix: 'dist/', + dontCacheBustUrlsMatching: /\.\w{6}\./ + }), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: '"production"' + } + }), + new UglifyJsPlugin({ + "uglifyOptions": + { + compress: { + warnings: false + }, + sourceMap: true, + output: { + ascii_only: true + } + } + } + ), + new webpack.LoaderOptionsPlugin({ + minimize: true + }) + ]) +} diff --git a/apps/pom.xml b/apps/pom.xml new file mode 100644 index 0000000000..7062206211 --- /dev/null +++ b/apps/pom.xml @@ -0,0 +1,53 @@ + + + + 4.0.0 + + + com.akto + root + ${revision} + + + com.akto.apps + apps + pom + + + + dashboard + + + dashboard/pom.xml + + + + dashboard + + + + api-runtime + + + api-runtime/pom.xml + + + + api-runtime + + + + testing + + + testing/pom.xml + + + + testing + + + + + diff --git a/apps/testing/Dockerfile b/apps/testing/Dockerfile new file mode 100644 index 0000000000..f7b368a635 --- /dev/null +++ b/apps/testing/Dockerfile @@ -0,0 +1,6 @@ +FROM amazoncorretto:8 +WORKDIR /app +COPY ./target/testing-1.0-SNAPSHOT-jar-with-dependencies.jar /app/testing-1.0-SNAPSHOT-jar-with-dependencies.jar +COPY ./src/main/resources/nuclei_linux /app/nuclei_linux +COPY ./src/main/resources/nuclei_m1 /app/nuclei_m1 +CMD "java" "-XX:+ExitOnOutOfMemoryError" "-jar" "/app/testing-1.0-SNAPSHOT-jar-with-dependencies.jar" diff --git a/apps/testing/pom.xml b/apps/testing/pom.xml new file mode 100644 index 0000000000..e4a6f1799a --- /dev/null +++ b/apps/testing/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + + + com.akto.apps + apps + ${revision} + + + com.akto.apps.testing + testing + jar + + + + + com.akto.libs.dao + dao + ${project.version} + + + com.akto.apps.api-runtime + api-runtime + ${project.version} + + + com.akto.libs.utils + utils + test-jar + ${project.version} + test + + + + org.yaml + snakeyaml + 1.33 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 8 + 8 + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.0.1 + + + copy-dependencies + package + + copy-dependencies + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + + + com.akto.testing.Main + + + + + jar-with-dependencies + + + + + + + + src/main/java + src/test/java + + + + \ No newline at end of file diff --git a/apps/testing/src/main/java/com/akto/rules/AddJkuToJwtTest.java b/apps/testing/src/main/java/com/akto/rules/AddJkuToJwtTest.java new file mode 100644 index 0000000000..b92e7b10d4 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/AddJkuToJwtTest.java @@ -0,0 +1,24 @@ +package com.akto.rules; + +import com.akto.util.JSONUtils; +import com.akto.util.modifier.AddJkuJWTModifier; + +import java.util.List; +import java.util.Map; + +public class AddJkuToJwtTest extends ModifyAuthTokenTestPlugin { + @Override + public Map> modifyHeaders(Map> headers) { + return JSONUtils.modifyHeaderValues(headers, new AddJkuJWTModifier()); + } + + @Override + public String superTestName() { + return "NO_AUTH"; + } + + @Override + public String subTestName() { + return "ADD_JKU_TO_JWT"; + } +} diff --git a/apps/testing/src/main/java/com/akto/rules/AddMethodInParameterTest.java b/apps/testing/src/main/java/com/akto/rules/AddMethodInParameterTest.java new file mode 100644 index 0000000000..ef9db7d487 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/AddMethodInParameterTest.java @@ -0,0 +1,29 @@ +package com.akto.rules; + +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.dto.type.URLMethods; + +public class AddMethodInParameterTest extends ChangeMethodPlugin { + @Override + public void modifyRequest(OriginalHttpRequest originalHttpRequest, URLMethods.Method method) { + String queryParams = originalHttpRequest.getQueryParams(); + if (queryParams == null) { + queryParams = "method="+method.name(); + } else { + queryParams = "&method="+method.name(); + } + + originalHttpRequest.setQueryParams(queryParams); + } + + @Override + public boolean isVulnerable(double percentageBodyMatch, int statusCode) { + return isStatusGood(statusCode) && percentageBodyMatch < 30; + } + + @Override + public String subTestName() { + return "ADD_METHOD_IN_PARAMETER"; + } +} diff --git a/apps/testing/src/main/java/com/akto/rules/AddMethodOverrideHeadersTest.java b/apps/testing/src/main/java/com/akto/rules/AddMethodOverrideHeadersTest.java new file mode 100644 index 0000000000..8111b7b957 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/AddMethodOverrideHeadersTest.java @@ -0,0 +1,29 @@ +package com.akto.rules; + +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.type.URLMethods; + +import java.util.Collections; +import java.util.List; + +public class AddMethodOverrideHeadersTest extends ChangeMethodPlugin { + + @Override + public void modifyRequest(OriginalHttpRequest originalHttpRequest, URLMethods.Method method) { + List value = Collections.singletonList(method.name()); + originalHttpRequest.getHeaders().put("x-http-method", value); + originalHttpRequest.getHeaders().put("x-http-method-override", value); + originalHttpRequest.getHeaders().put("x-method-override", value); + } + + @Override + public boolean isVulnerable(double percentageBodyMatch, int statusCode) { + return isStatusGood(statusCode) && percentageBodyMatch < 30; + } + + @Override + public String subTestName() { + return "ADD_METHOD_OVERRIDE_HEADERS"; + } + +} diff --git a/apps/testing/src/main/java/com/akto/rules/AddUserIdTest.java b/apps/testing/src/main/java/com/akto/rules/AddUserIdTest.java new file mode 100644 index 0000000000..99acbc8f4e --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/AddUserIdTest.java @@ -0,0 +1,80 @@ +package com.akto.rules; + +import com.akto.dto.ApiInfo; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.dto.RawApi; +import com.akto.dto.testing.AuthMechanism; +import com.akto.dto.testing.TestResult; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.store.SampleMessageStore; +import com.akto.store.TestingUtil; +import com.akto.types.CappedSet; + +import java.util.*; + +public class AddUserIdTest extends AuthRequiredTestPlugin{ + + @Override + public Result exec(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil, List filteredMessages) { + List userIdNameList = Arrays.asList( + "user", "User", "userId", "UserId", "user_id", "customer_id", "customerId", "CustomerId", "customer", + "user_name", "username", "UserName","customer_name" + ); + + Map validUserIdNameMap = new HashMap<>(); + for (SingleTypeInfo singleTypeInfo: testingUtil.getSingleTypeInfoMap().values()) { + String param = singleTypeInfo.getParam(); + String key = SingleTypeInfo.findLastKeyFromParam(param); + if (key == null) continue; + + CappedSet values = singleTypeInfo.getValues(); + if (values.count() == 0) continue; + if (userIdNameList.contains(key)) validUserIdNameMap.put(key,singleTypeInfo); + } + + if (validUserIdNameMap.isEmpty()) return null; + + RawApi rawApi = filteredMessages.get(0); + List testResults = new ArrayList<>(); + OriginalHttpRequest testRequest = rawApi.getRequest().copy(); + + for (String key: validUserIdNameMap.keySet()) { + SingleTypeInfo singleTypeInfo = validUserIdNameMap.get(key); + CappedSet values = singleTypeInfo.getValues(); + if (values.count() == 0) continue; + String val = (String) values.getElements().toArray()[0]; + String combinedQueryParams = OriginalHttpRequest.combineQueryParams(testRequest.getQueryParams(), key + "=" + val ); + testRequest.setQueryParams(combinedQueryParams); + } + + ApiExecutionDetails apiExecutionDetails; + try { + apiExecutionDetails = executeApiAndReturnDetails(testRequest, true, rawApi); + } catch (Exception e) { + return addWithRequestError( rawApi.getOriginalMessage(), TestResult.TestError.API_REQUEST_FAILED, testRequest, null); + } + + boolean vulnerable = isStatusGood(apiExecutionDetails.statusCode) && apiExecutionDetails.percentageMatch < 50; + + TestResult testResult = buildTestResult( + testRequest, apiExecutionDetails.testResponse, apiExecutionDetails.originalReqResp, apiExecutionDetails.percentageMatch, vulnerable, null + ); + + testResults.add(testResult); + + return addTestSuccessResult(vulnerable, testResults, new ArrayList<>(), TestResult.Confidence.HIGH); + + } + + + @Override + public String superTestName() { + return "ADD_USER_ID"; + } + + @Override + public String subTestName() { + return "ADD_USER_ID"; + } +} diff --git a/apps/testing/src/main/java/com/akto/rules/AuthRequiredRunAllTestPlugin.java b/apps/testing/src/main/java/com/akto/rules/AuthRequiredRunAllTestPlugin.java new file mode 100644 index 0000000000..6a53b4bb9b --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/AuthRequiredRunAllTestPlugin.java @@ -0,0 +1,35 @@ +package com.akto.rules; + +import com.akto.dto.ApiInfo; +import com.akto.dto.RawApi; +import com.akto.store.TestingUtil; + +import java.util.List; + +public abstract class AuthRequiredRunAllTestPlugin extends AuthRequiredTestPlugin { + @Override + public Result exec(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil, List filteredMessages) { + + boolean vulnerable = false; + List results = null; + + for (RawApi rawApi: filteredMessages) { + if (vulnerable) break; + results = execute(rawApi, apiInfoKey, testingUtil); + for (ExecutorResult result: results) { + if (result.vulnerable) { + vulnerable = true; + break; + } + } + } + + if (results == null) return null; + + return convertExecutorResultsToResult(results); + + } + + public abstract List execute(RawApi rawApi, ApiInfo.ApiInfoKey apiInfoKey , TestingUtil testingUtil); + +} diff --git a/apps/testing/src/main/java/com/akto/rules/AuthRequiredTestPlugin.java b/apps/testing/src/main/java/com/akto/rules/AuthRequiredTestPlugin.java new file mode 100644 index 0000000000..73972a5d63 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/AuthRequiredTestPlugin.java @@ -0,0 +1,24 @@ +package com.akto.rules; + +import com.akto.dto.ApiInfo; +import com.akto.dto.RawApi; +import com.akto.store.SampleMessageStore; +import com.akto.store.TestingUtil; + +import java.util.List; + +public abstract class AuthRequiredTestPlugin extends TestPlugin { + + @Override + public Result start(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil) { + List messages = SampleMessageStore.fetchAllOriginalMessages(apiInfoKey, testingUtil.getSampleMessages()); + if (messages.isEmpty()) return null; + List filteredMessages = SampleMessageStore.filterMessagesWithAuthToken(messages, testingUtil.getAuthMechanism()); + if (filteredMessages.isEmpty()) return null; + + return exec(apiInfoKey, testingUtil, filteredMessages); + } + + public abstract Result exec(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil, List filteredMessages); + +} diff --git a/apps/testing/src/main/java/com/akto/rules/BFLATest.java b/apps/testing/src/main/java/com/akto/rules/BFLATest.java new file mode 100644 index 0000000000..1f503c11b4 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/BFLATest.java @@ -0,0 +1,68 @@ +package com.akto.rules; + +import com.akto.dto.ApiInfo; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.dto.RawApi; +import com.akto.dto.testing.TestResult; +import com.akto.dto.testing.TestRoles; +import com.akto.dto.testing.info.BFLATestInfo; +import com.akto.store.TestingUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class BFLATest extends AuthRequiredRunAllTestPlugin { + + public List execute(RawApi rawApi, ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil) { + TestPlugin.TestRoleMatcher testRoleMatcher = new TestPlugin.TestRoleMatcher(testingUtil.getTestRoles(), apiInfoKey); + + TestRoles normalUserTestRole = new TestRoles(); + normalUserTestRole.setAuthMechanism(testingUtil.getAuthMechanism()); + testRoleMatcher.enemies.add(normalUserTestRole); + + List executorResults = new ArrayList<>(); + + for (TestRoles testRoles: testRoleMatcher.enemies) { + OriginalHttpRequest testRequest = rawApi.getRequest().copy(); + + testRoles.getAuthMechanism().addAuthToRequest(testRequest); + BFLATestInfo bflaTestInfo = new BFLATestInfo( + "NORMAL", testingUtil.getTestRoles().get(0).getName() + ); + + ApiExecutionDetails apiExecutionDetails; + RawApi rawApiDuplicate = rawApi.copy(); + try { + apiExecutionDetails = executeApiAndReturnDetails(testRequest, true, rawApiDuplicate); + } catch (Exception e) { + return Collections.singletonList(new ExecutorResult(false, null, new ArrayList<>(), 0, rawApi, + TestResult.TestError.API_REQUEST_FAILED, testRequest, null, bflaTestInfo)); + } + + boolean vulnerable = isStatusGood(apiExecutionDetails.statusCode); + TestResult.Confidence confidence = vulnerable ? TestResult.Confidence.HIGH : TestResult.Confidence.LOW; + + ExecutorResult executorResult = new ExecutorResult(vulnerable,confidence, null, apiExecutionDetails.percentageMatch, + rawApiDuplicate, null, testRequest, apiExecutionDetails.testResponse, bflaTestInfo); + + executorResults.add(executorResult); + } + + return executorResults; + + } + + @Override + public String superTestName() { + return "BFLA"; + } + + @Override + public String subTestName() { + return "BFLA"; + } + +} diff --git a/apps/testing/src/main/java/com/akto/rules/BOLATest.java b/apps/testing/src/main/java/com/akto/rules/BOLATest.java new file mode 100644 index 0000000000..e84accf7a3 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/BOLATest.java @@ -0,0 +1,97 @@ +package com.akto.rules; + +import com.akto.dto.*; +import com.akto.dto.testing.AuthMechanism; +import com.akto.dto.testing.TestResult; +import com.akto.dto.testing.TestResult.Confidence; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.store.SampleMessageStore; +import com.akto.store.TestingUtil; +import com.akto.testing.ApiExecutor; +import com.akto.testing.StatusCodeAnalyser; +import com.akto.util.JSONUtils; +import com.akto.util.modifier.ConvertToArrayPayloadModifier; +import com.akto.util.modifier.NestedObjectModifier; + +import java.util.*; + +public class BOLATest extends AuthRequiredRunAllTestPlugin { + + public BOLATest() { } + + @Override + public String superTestName() { + return "BOLA"; + } + + @Override + public String subTestName() { + return "REPLACE_AUTH_TOKEN"; + } + + + @Override + public List execute(RawApi rawApi, ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil) { + OriginalHttpRequest testRequest = rawApi.getRequest().copy(); + + testingUtil.getAuthMechanism().addAuthToRequest(testRequest); + + ContainsPrivateResourceResult containsPrivateResourceResult = containsPrivateResource(testRequest, apiInfoKey, testingUtil.getSingleTypeInfoMap()); + // We consider API contains private resources if : + // a) Contains 1 or more private resources + // b) We couldn't find uniqueCount or publicCount for some request params + // When it comes to case b we still say private resource but with low confidence, hence the below line + + ExecutorResult executorResultSimple = util(testRequest, rawApi, containsPrivateResourceResult); + + List executorResults = new ArrayList<>(); + executorResults.add(executorResultSimple); + + Set privateStiParams = containsPrivateResourceResult.findPrivateParams(); + if (testRequest.isJsonRequest()) { + OriginalHttpRequest testRequestArray = testRequest.copy(); + String modifiedPayload = JSONUtils.modify(testRequestArray.getJsonRequestBody(), privateStiParams, new ConvertToArrayPayloadModifier()); + if (modifiedPayload != null) { + testRequestArray.setBody(modifiedPayload); + ExecutorResult executorResultArray = util(testRequestArray, rawApi, containsPrivateResourceResult); + executorResults.add(executorResultArray); + } + + OriginalHttpRequest testRequestJson = testRequest.copy(); + modifiedPayload = JSONUtils.modify(testRequestJson.getJsonRequestBody(), privateStiParams, new NestedObjectModifier()); + if (modifiedPayload != null) { + testRequestJson.setBody(modifiedPayload); + ExecutorResult executorResultJson = util(testRequestJson, rawApi, containsPrivateResourceResult); + executorResults.add(executorResultJson); + } + } + + + return executorResults; + } + + public ExecutorResult util(OriginalHttpRequest testRequest, RawApi rawApi, ContainsPrivateResourceResult containsPrivateResourceResult) { + + ApiExecutionDetails apiExecutionDetails; + RawApi rawApiDuplicate = rawApi.copy(); + try { + apiExecutionDetails = executeApiAndReturnDetails(testRequest, true, rawApiDuplicate); + } catch (Exception e) { + return new ExecutorResult(false, null, new ArrayList<>(), 0, rawApi, + TestResult.TestError.API_REQUEST_FAILED, testRequest, null, null); + } + + TestResult.Confidence confidence = containsPrivateResourceResult.findPrivateOnes().size() > 0 ? TestResult.Confidence.HIGH : TestResult.Confidence.LOW; + + boolean vulnerable = isStatusGood(apiExecutionDetails.statusCode) && containsPrivateResourceResult.isPrivate && apiExecutionDetails.percentageMatch > 90; + + // We can say with high confidence if an api is not vulnerable, and we don't need help of private resources for this + if (!vulnerable) confidence = Confidence.HIGH; + + return new ExecutorResult(vulnerable,confidence, containsPrivateResourceResult.singleTypeInfos, apiExecutionDetails.percentageMatch, + rawApiDuplicate, null, testRequest, apiExecutionDetails.testResponse, null); + + } + + +} diff --git a/apps/testing/src/main/java/com/akto/rules/ChangeHttpMethodTest.java b/apps/testing/src/main/java/com/akto/rules/ChangeHttpMethodTest.java new file mode 100644 index 0000000000..f8ffdc0ba6 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/ChangeHttpMethodTest.java @@ -0,0 +1,23 @@ +package com.akto.rules; + +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.type.URLMethods; + +public class ChangeHttpMethodTest extends ChangeMethodPlugin { + + @Override + public void modifyRequest(OriginalHttpRequest originalHttpRequest, URLMethods.Method method) { + originalHttpRequest.setMethod(method.name()); + } + + @Override + public boolean isVulnerable(double percentageBodyMatch, int statusCode) { + return isStatusGood(statusCode); + } + + @Override + public String subTestName() { + return "CHANGE_METHOD"; + } + +} diff --git a/apps/testing/src/main/java/com/akto/rules/ChangeMethodPlugin.java b/apps/testing/src/main/java/com/akto/rules/ChangeMethodPlugin.java new file mode 100644 index 0000000000..b169c8ce21 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/ChangeMethodPlugin.java @@ -0,0 +1,68 @@ +package com.akto.rules; + +import com.akto.dto.ApiInfo; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.dto.RawApi; +import com.akto.dto.testing.AuthMechanism; +import com.akto.dto.testing.TestResult; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods; +import com.akto.store.SampleMessageStore; +import com.akto.store.TestingUtil; +import com.akto.testing.ApiExecutor; +import com.akto.testing.StatusCodeAnalyser; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class ChangeMethodPlugin extends TestPlugin { + + public abstract void modifyRequest(OriginalHttpRequest originalHttpRequest, URLMethods.Method method); + + public abstract boolean isVulnerable(double percentageBodyMatch, int statusCode); + + @Override + public Result start(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil) { + + List undocumentedMethods = findUndocumentedMethods( testingUtil.getSampleMessages(), apiInfoKey); + + List messages = SampleMessageStore.fetchAllOriginalMessages(apiInfoKey, testingUtil.getSampleMessages()); + if (messages.isEmpty()) return null; + + RawApi rawApi = messages.get(0); + + boolean overallVulnerable = false; + + List testResults = new ArrayList<>(); + for (URLMethods.Method method: undocumentedMethods) { + OriginalHttpRequest testRequest = rawApi.getRequest().copy(); + + modifyRequest(testRequest, method); + + ApiExecutionDetails apiExecutionDetails; + TestResult testResult; + try { + apiExecutionDetails = executeApiAndReturnDetails(testRequest, true, rawApi); + boolean vulnerable = isVulnerable(apiExecutionDetails.percentageMatch, apiExecutionDetails.statusCode); + overallVulnerable = overallVulnerable || vulnerable; + + testResult = buildTestResult(testRequest, apiExecutionDetails.testResponse, apiExecutionDetails.originalReqResp, apiExecutionDetails.percentageMatch, vulnerable, null); + } catch (Exception e) { + testResult = buildFailedTestResultWithOriginalMessage( rawApi.getOriginalMessage(), TestResult.TestError.API_REQUEST_FAILED, testRequest, null); + } + + testResults.add(testResult); + } + + return addTestSuccessResult(overallVulnerable, testResults, new ArrayList<>(), TestResult.Confidence.HIGH); + + } + + @Override + public String superTestName() { + return "PRIVILEGE_ESCALATION"; + } +} diff --git a/apps/testing/src/main/java/com/akto/rules/FuzzingTest.java b/apps/testing/src/main/java/com/akto/rules/FuzzingTest.java new file mode 100644 index 0000000000..86a6ab5b14 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/FuzzingTest.java @@ -0,0 +1,178 @@ +package com.akto.rules; + +import com.akto.dto.ApiInfo; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.dto.RawApi; +import com.akto.dto.testing.TestResult; +import com.akto.dto.testing.info.NucleiTestInfo; +import com.akto.store.SampleMessageStore; +import com.akto.store.TestingUtil; +import com.akto.testing.NucleiExecutor; +import com.akto.testing.StatusCodeAnalyser; +import com.akto.util.Pair; +import com.google.common.io.Files; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.yaml.snakeyaml.Yaml; + +import java.io.*; +import java.net.URL; +import java.util.*; + +public class FuzzingTest extends TestPlugin { + + private final String testRunId; + private final String testRunResultSummaryId; + private final String origTemplatePath; + private String tempTemplatePath; + private final String subcategory; + private String testSourceConfigCategory; + + public FuzzingTest(String testRunId, String testRunResultSummaryId, String origTemplatePath, String subcategory, String testSourceConfigCategory) { + this.testRunId = testRunId; + this.testRunResultSummaryId = testRunResultSummaryId; + this.origTemplatePath = origTemplatePath; + this.subcategory = subcategory; + this.testSourceConfigCategory = testSourceConfigCategory; + this.tempTemplatePath = null; + } + + public static File createDirPath(String filePath) { + try { + File file = new File(filePath); + Files.createParentDirs(file); + return file; + } catch (IOException e) { + return null; + } + } + + @Override + public Result start(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil) { + List messages = SampleMessageStore.fetchAllOriginalMessages(apiInfoKey, testingUtil.getSampleMessages()); + if (messages.isEmpty()) return null; + + RawApi rawApi = messages.get(0); + List testResults = new ArrayList<>(); + OriginalHttpRequest testRequest = rawApi.getRequest().copy(); + + OriginalHttpResponse originalHttpResponse = rawApi.getResponse().copy(); + + ApiExecutionDetails apiExecutionDetails; + + String subDir = ""+testRequest.hashCode(); + + String pwd = new File("").getAbsolutePath(); + + String filepath = StringUtils.join(new String[]{pwd, testRunId, testRunResultSummaryId, subDir, "logs.txt"}, "/"); + File file = createDirPath(filepath); + + if (file == null) return null; + boolean vulnerable = false; + String outputDir = file.getParent(); + this.tempTemplatePath = outputDir+"/"+subcategory+".yaml"; + NucleiTestInfo nucleiTestInfo = new NucleiTestInfo(this.subcategory, this.origTemplatePath); + + try { + FileUtils.copyURLToFile(new URL(this.origTemplatePath), new File(this.tempTemplatePath)); + } catch (IOException e1) { + return addWithRequestError( rawApi.getOriginalMessage(), TestResult.TestError.FAILED_DOWNLOADING_NUCLEI_TEMPLATE, testRequest, nucleiTestInfo); + } + + try { + downloadLinks(this.tempTemplatePath, outputDir); + } catch (Exception e) { + ; + return addWithRequestError( rawApi.getOriginalMessage(), TestResult.TestError.FAILED_DOWNLOADING_PAYLOAD_FILES, testRequest, nucleiTestInfo); + } + + try { + NucleiExecutor.NucleiResult nucleiResult = NucleiExecutor.execute( + testRequest.getMethod(), + testRequest.getFullUrlWithParams(), + this.tempTemplatePath, + outputDir, + testRequest.getBody(), + testRequest.getHeaders(), + pwd + ); + + if (nucleiResult == null) return addWithoutRequestError(rawApi.getOriginalMessage(), TestResult.TestError.FAILED_BUILDING_NUCLEI_TEMPLATE); + + int totalCount = Math.min(Math.min(nucleiResult.attempts.size(), nucleiResult.metaData.size()), 100); + + for (int idx=0; idx < totalCount; idx++) { + Pair pair = nucleiResult.attempts.get(idx); + OriginalHttpResponse testResponse = pair.getSecond(); + + int statusCode = StatusCodeAnalyser.getStatusCode(testResponse.getBody(), testResponse.getStatusCode()); + double percentageMatch = compareWithOriginalResponse(originalHttpResponse.getBody(), testResponse.getBody(), new HashMap<>()); + + vulnerable = nucleiResult.metaData.get(idx).getBoolean("matcher-status"); + + apiExecutionDetails = new ApiExecutionDetails(statusCode, percentageMatch, testResponse, originalHttpResponse, rawApi.getOriginalMessage()); + + TestResult testResult = buildTestResult( + pair.getFirst(), apiExecutionDetails.testResponse, rawApi.getOriginalMessage(), apiExecutionDetails.percentageMatch, vulnerable, nucleiTestInfo + ); + + testResults.add(testResult); + + } + } catch (Exception e) { + ; + return addWithRequestError( rawApi.getOriginalMessage(), TestResult.TestError.API_REQUEST_FAILED, testRequest, nucleiTestInfo); + } + + return addTestSuccessResult(vulnerable, testResults, new ArrayList<>(), TestResult.Confidence.HIGH); + } + + public static void downloadLinks(String templatePath, String outputDir) throws IOException { + List urlsToDownload = new ArrayList<>(); + Yaml yaml = new Yaml(); + InputStream inputStream = java.nio.file.Files.newInputStream(new File(templatePath).toPath()); + Map data = yaml.load(inputStream); + if (data == null) data = new HashMap<>(); + List> requests = (List>) data.get("requests"); + if (requests == null) requests = new ArrayList<>(); + for (Map request: requests) { + Map payloads = (Map) request.get("payloads"); + if (payloads == null) payloads = new HashMap<>(); + for (Object payload: payloads.values()) { + if (payload.getClass().equals(String.class)) { + String payloadString = (String) payload; + if (payloadString.startsWith("http")) urlsToDownload.add(payloadString); + } + } + } + + for (String url: urlsToDownload) { + String[] fileNameList = url.split("/"); + FileUtils.copyURLToFile(new URL(url), new File(outputDir + "/" + fileNameList[fileNameList.length-1])); + } + } + + public String getSubcategory() { + return subcategory; + } + + @Override + public String superTestName() { + return "FUZZING"; + } + + @Override + public String subTestName() { + return "CUSTOM_IAM"; + } + + public String getTestSourceConfigCategory() { + return testSourceConfigCategory; + } + + public void setTestSourceConfigCategory(String testSourceConfigCategory) { + this.testSourceConfigCategory = testSourceConfigCategory; + } +} diff --git a/apps/testing/src/main/java/com/akto/rules/JWTInvalidSignatureTest.java b/apps/testing/src/main/java/com/akto/rules/JWTInvalidSignatureTest.java new file mode 100644 index 0000000000..c45f5b1383 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/JWTInvalidSignatureTest.java @@ -0,0 +1,25 @@ +package com.akto.rules; + +import com.akto.util.JSONUtils; +import com.akto.util.modifier.InvalidSignatureJWTModifier; + +import java.util.List; +import java.util.Map; + +public class JWTInvalidSignatureTest extends ModifyAuthTokenTestPlugin { + + public Map> modifyHeaders(Map> headers) { + return JSONUtils.modifyHeaderValues(headers, new InvalidSignatureJWTModifier()); + } + + + @Override + public String superTestName() { + return "NO_AUTH"; + } + + @Override + public String subTestName() { + return "JWT_INVALID_SIGNATURE"; + } +} diff --git a/apps/testing/src/main/java/com/akto/rules/JWTNoneAlgoTest.java b/apps/testing/src/main/java/com/akto/rules/JWTNoneAlgoTest.java new file mode 100644 index 0000000000..78176a6ccf --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/JWTNoneAlgoTest.java @@ -0,0 +1,36 @@ +package com.akto.rules; + +import com.akto.dto.ApiInfo; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.dto.RawApi; +import com.akto.dto.testing.AuthMechanism; +import com.akto.dto.testing.TestResult; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.store.SampleMessageStore; +import com.akto.store.TestingUtil; +import com.akto.util.JSONUtils; +import com.akto.util.modifier.NoneAlgoJWTModifier; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class JWTNoneAlgoTest extends ModifyAuthTokenTestPlugin { + + public Map> modifyHeaders(Map> headers) { + return JSONUtils.modifyHeaderValues(headers, new NoneAlgoJWTModifier()); + } + + + @Override + public String superTestName() { + return "NO_AUTH"; + } + + @Override + public String subTestName() { + return "JWT_NONE_ALGO"; + } +} diff --git a/apps/testing/src/main/java/com/akto/rules/ModifyAuthTokenTestPlugin.java b/apps/testing/src/main/java/com/akto/rules/ModifyAuthTokenTestPlugin.java new file mode 100644 index 0000000000..f324ed91d4 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/ModifyAuthTokenTestPlugin.java @@ -0,0 +1,47 @@ +package com.akto.rules; + +import com.akto.dto.ApiInfo; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.dto.RawApi; +import com.akto.dto.testing.TestResult; +import com.akto.store.TestingUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public abstract class ModifyAuthTokenTestPlugin extends AuthRequiredTestPlugin { + + public abstract Map> modifyHeaders(Map> headers); + @Override + public Result exec(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil, List filteredMessages) { + RawApi rawApi = filteredMessages.get(0).copy(); + + OriginalHttpRequest testRequest = rawApi.getRequest(); + + Map> modifiedHeaders = modifyHeaders(testRequest.getHeaders()); + if (modifiedHeaders == null || modifiedHeaders.isEmpty()) return null; + testRequest.setHeaders(modifiedHeaders); + + ApiExecutionDetails apiExecutionDetails; + try { + apiExecutionDetails = executeApiAndReturnDetails(testRequest, true, rawApi); + } catch (Exception e) { + return addWithRequestError( rawApi.getOriginalMessage(), TestResult.TestError.API_REQUEST_FAILED, testRequest, null); + } + + boolean vulnerable = isStatusGood(apiExecutionDetails.statusCode); + + TestResult testResult = buildTestResult( + testRequest, apiExecutionDetails.testResponse, apiExecutionDetails.originalReqResp, apiExecutionDetails.percentageMatch, vulnerable, null + ); + + return addTestSuccessResult( + vulnerable, Collections.singletonList(testResult), new ArrayList<>(), TestResult.Confidence.HIGH + ); + + } + +} diff --git a/apps/testing/src/main/java/com/akto/rules/NoAuthTest.java b/apps/testing/src/main/java/com/akto/rules/NoAuthTest.java new file mode 100644 index 0000000000..8055afa139 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/NoAuthTest.java @@ -0,0 +1,54 @@ +package com.akto.rules; + + +import com.akto.dto.*; +import com.akto.dto.testing.*; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.store.SampleMessageStore; +import com.akto.store.TestingUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + + +public class NoAuthTest extends AuthRequiredTestPlugin { + + @Override + public Result exec(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil, List filteredMessages) { + RawApi rawApi = filteredMessages.get(0).copy(); + + OriginalHttpRequest testRequest = rawApi.getRequest(); + + testingUtil.getAuthMechanism().removeAuthFromRequest(testRequest); + + ApiExecutionDetails apiExecutionDetails; + try { + apiExecutionDetails = executeApiAndReturnDetails(testRequest, true, rawApi); + } catch (Exception e) { + return addWithRequestError( rawApi.getOriginalMessage(), TestResult.TestError.API_REQUEST_FAILED, testRequest, null); + } + + boolean vulnerable = isStatusGood(apiExecutionDetails.statusCode); + + TestResult testResult = buildTestResult( + testRequest, apiExecutionDetails.testResponse, apiExecutionDetails.originalReqResp, apiExecutionDetails.percentageMatch, vulnerable, null + ); + return addTestSuccessResult( + vulnerable, Collections.singletonList(testResult), new ArrayList<>(), TestResult.Confidence.HIGH + ); + + } + + @Override + public String superTestName() { + return "NO_AUTH"; + } + + @Override + public String subTestName() { + return "REMOVE_TOKENS"; + } + +} diff --git a/apps/testing/src/main/java/com/akto/rules/OldApiVersionTest.java b/apps/testing/src/main/java/com/akto/rules/OldApiVersionTest.java new file mode 100644 index 0000000000..7b9343afe0 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/OldApiVersionTest.java @@ -0,0 +1,79 @@ +package com.akto.rules; + +import com.akto.dto.ApiInfo; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.dto.RawApi; +import com.akto.dto.testing.AuthMechanism; +import com.akto.dto.testing.HardcodedAuthParam; +import com.akto.dto.testing.TestResult; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.store.SampleMessageStore; +import com.akto.store.TestingUtil; +import com.akto.testing.ApiExecutor; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class OldApiVersionTest extends AuthRequiredTestPlugin{ + @Override + public Result exec(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil, List filteredMessages) { + String url = apiInfoKey.getUrl(); + String oldVersionUrl = decrementUrlVersion(url, 1, 1); + if (oldVersionUrl == null) return null; + + RawApi rawApi = filteredMessages.get(0).copy(); + Result result = null; + + int i = 0; + while (oldVersionUrl != null) { + if (i >= 10) break; + ApiInfo.ApiInfoKey oldVersionApiInfoKey = new ApiInfo.ApiInfoKey(apiInfoKey.getApiCollectionId(), oldVersionUrl, apiInfoKey.getMethod()); + + // ignore if exists in traffic data + if (testingUtil.getSampleMessages().containsKey(oldVersionApiInfoKey)) { + oldVersionUrl = decrementUrlVersion(oldVersionUrl, 1, 1); + continue; + } + + i += 1; + + // change the url to oldVersionUrl + OriginalHttpRequest testRequest = rawApi.getRequest().copy(); + testRequest.setUrl(oldVersionUrl); + + // hit the older version api once without changing anything else to get base condition to compare against + OriginalHttpResponse originalHttpResponse; + try { + originalHttpResponse = ApiExecutor.sendRequest(testRequest, true); + } catch (Exception e) { + ; + oldVersionUrl = decrementUrlVersion(oldVersionUrl, 1, 1); + continue; + } + + // try BOLA + BOLATest bolaTest = new BOLATest(); + RawApi dummy = new RawApi(testRequest, originalHttpResponse, rawApi.getOriginalMessage()); + List executorResults = bolaTest.execute(dummy, apiInfoKey, testingUtil); + result = convertExecutorResultsToResult(executorResults); + + if (result.isVulnerable) return result; + + oldVersionUrl = decrementUrlVersion(oldVersionUrl, 1, 1); + } + + return result; + } + + @Override + public String superTestName() { + return "BOLA"; + } + + @Override + public String subTestName() { + return "REPLACE_AUTH_TOKEN_OLD_VERSION"; + } +} diff --git a/apps/testing/src/main/java/com/akto/rules/ParameterPollutionTest.java b/apps/testing/src/main/java/com/akto/rules/ParameterPollutionTest.java new file mode 100644 index 0000000000..04c15a702a --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/ParameterPollutionTest.java @@ -0,0 +1,81 @@ +package com.akto.rules; + +import com.akto.dto.*; +import com.akto.dto.testing.*; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.store.SampleMessageStore; +import com.akto.store.TestingUtil; +import com.akto.types.CappedSet; + +import java.util.*; + +public class ParameterPollutionTest extends TestPlugin { + + public ParameterPollutionTest() {} + + + @Override + public Result start(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil) { + List messages = SampleMessageStore.fetchAllOriginalMessages(apiInfoKey, testingUtil.getSampleMessages()); + if (messages.isEmpty()) return null; + List filteredMessages = SampleMessageStore.filterMessagesWithAuthToken(messages, testingUtil.getAuthMechanism()); + if (filteredMessages.size() < 2) return addWithoutRequestError(null, TestResult.TestError.INSUFFICIENT_MESSAGES); + + RawApi message1 = filteredMessages.get(0).copy(); + RawApi message2 = filteredMessages.get(1).copy(); + + OriginalHttpRequest testRequest1 = message1.getRequest(); + OriginalHttpRequest testRequest2 = message2.getRequest(); + + ContainsPrivateResourceResult containsPrivateResourceResult2 = containsPrivateResource(testRequest2, apiInfoKey, testingUtil.getSingleTypeInfoMap()); + + boolean atLeastOneParam = false; + for (SingleTypeInfo singleTypeInfo: containsPrivateResourceResult2.findPrivateOnes()) { + if (singleTypeInfo.getIsUrlParam()) continue; + + String param = SingleTypeInfo.findLastKeyFromParam(singleTypeInfo.getParam()); + if (param == null || param.trim().length() < 1) continue; + + CappedSet values = singleTypeInfo.getValues(); + if (values.count() == 0) continue; + + String value = values.getElements().toArray()[0].toString(); + atLeastOneParam = true; + + String ogQuery = testRequest1.getQueryParams(); + String combinedQueryParams = OriginalHttpRequest.combineQueryParams(ogQuery, param+"="+value); + testRequest1.setQueryParams(combinedQueryParams); + } + + if (!atLeastOneParam) return null; + + ApiExecutionDetails apiExecutionDetails; + try { + apiExecutionDetails = executeApiAndReturnDetails(testRequest1, true, message1); + } catch (Exception e) { + return addWithRequestError( message1.getOriginalMessage(), TestResult.TestError.API_REQUEST_FAILED, testRequest1, null); + } + + boolean vulnerable = isStatusGood(apiExecutionDetails.statusCode) && apiExecutionDetails.percentageMatch < 80; + + TestResult testResult = buildTestResult( + testRequest1, apiExecutionDetails.testResponse, apiExecutionDetails.originalReqResp, apiExecutionDetails.percentageMatch, vulnerable, null + ); + return addTestSuccessResult( + vulnerable, Collections.singletonList(testResult), new ArrayList<>(), TestResult.Confidence.HIGH + ); + + + } + + @Override + public String superTestName() { + return "BOLA"; + } + + @Override + public String subTestName() { + return "PARAMETER_POLLUTION"; + } + +} diff --git a/apps/testing/src/main/java/com/akto/rules/TestPlugin.java b/apps/testing/src/main/java/com/akto/rules/TestPlugin.java new file mode 100644 index 0000000000..ee0cb31d9f --- /dev/null +++ b/apps/testing/src/main/java/com/akto/rules/TestPlugin.java @@ -0,0 +1,553 @@ +package com.akto.rules; + +import com.akto.dto.*; +import com.akto.dto.ApiInfo.ApiInfoKey; +import com.akto.dto.testing.*; +import com.akto.dto.testing.info.TestInfo; +import com.akto.dto.type.*; +import com.akto.runtime.APICatalogSync; +import com.akto.runtime.RelationshipSync; +import com.akto.store.TestingUtil; +import com.akto.testing.ApiExecutor; +import com.akto.testing.StatusCodeAnalyser; +import com.akto.types.CappedSet; +import com.akto.util.JSONUtils; +import com.akto.utils.RedactSampleData; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.mongodb.BasicDBObject; + +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.akto.runtime.APICatalogSync.trimAndSplit; + + +public abstract class TestPlugin { + static ObjectMapper mapper = new ObjectMapper(); + static JsonFactory factory = mapper.getFactory(); + + private static final Logger logger = LoggerFactory.getLogger(TestPlugin.class); + private static final Gson gson = new Gson(); + + public abstract Result start(ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil); + + public abstract String superTestName(); + public abstract String subTestName(); + + public static boolean isStatusGood(int statusCode) { + return statusCode >= 200 && statusCode<300; + } + + public static void extractAllValuesFromPayload(String payload, Map> payloadMap) throws Exception{ + JsonParser jp = factory.createParser(payload); + JsonNode node = mapper.readTree(jp); + RelationshipSync.extractAllValuesFromPayload(node,new ArrayList<>(),payloadMap); + } + + public static String decrementUrlVersion(String url, int decrementValue, int limit) { + String regex = "\\/v(\\d+)\\/"; + Pattern p = Pattern.compile(regex); + Matcher matcher = p.matcher(url); + StringBuffer sb = new StringBuffer(); + + boolean containsAtLeastOneVersion = false; + + while (matcher.find()) { + String code = matcher.group(1); + int version; + try { + version = Integer.parseInt(code); + } catch (Exception e) { + ; + return null; + } + int newVersion = version - decrementValue; + if (newVersion < limit) return null; + containsAtLeastOneVersion = true; + matcher.appendReplacement(sb, "/v"+newVersion+"/"); + } + + if (!containsAtLeastOneVersion) return null; + + matcher.appendTail(sb); + + return sb.toString(); + } + + public static double compareWithOriginalResponse(String originalPayload, String currentPayload, Map comparisonExcludedKeys) { + if (originalPayload == null && currentPayload == null) return 100; + if (originalPayload == null || currentPayload == null) return 0; + + String trimmedOriginalPayload = originalPayload.trim(); + String trimmedCurrentPayload = currentPayload.trim(); + if (trimmedCurrentPayload.equals(trimmedOriginalPayload)) return 100; + + Map> originalResponseParamMap = new HashMap<>(); + Map> currentResponseParamMap = new HashMap<>(); + try { + extractAllValuesFromPayload(originalPayload, originalResponseParamMap); + extractAllValuesFromPayload(currentPayload, currentResponseParamMap); + } catch (Exception e) { + return 0.0; + } + + if (originalResponseParamMap.keySet().size() == 0 && currentResponseParamMap.keySet().size() == 0) { + return 100.0; + } + + Set visited = new HashSet<>(); + int matched = 0; + for (String k1: originalResponseParamMap.keySet()) { + if (visited.contains(k1) || comparisonExcludedKeys.containsKey(k1)) continue; + visited.add(k1); + Set v1 = originalResponseParamMap.get(k1); + Set v2 = currentResponseParamMap.get(k1); + if (Objects.equals(v1, v2)) matched +=1; + } + + for (String k1: currentResponseParamMap.keySet()) { + if (visited.contains(k1) || comparisonExcludedKeys.containsKey(k1)) continue; + visited.add(k1); + Set v1 = originalResponseParamMap.get(k1); + Set v2 = currentResponseParamMap.get(k1); + if (Objects.equals(v1, v2)) matched +=1; + } + + int visitedSize = visited.size(); + if (visitedSize == 0) return 0.0; + + double result = (100.0*matched)/visitedSize; + + if (Double.isFinite(result)) { + return result; + } else { + return 0.0; + } + + } + + public Result addWithoutRequestError(String originalMessage, TestResult.TestError testError) { + List testResults = new ArrayList<>(); + testResults.add(new TestResult(null, originalMessage, Collections.singletonList(testError), 0, false, TestResult.Confidence.HIGH, null)); + return new Result(testResults, false,new ArrayList<>(), 0); + } + + public TestResult buildFailedTestResultWithOriginalMessage(String originalMessage, TestResult.TestError testError, OriginalHttpRequest request, TestInfo testInfo) { + String message = null; + try { + message = RedactSampleData.convertOriginalReqRespToString(request, null); + } catch (Exception e) { + ; + } + + return new TestResult(message, originalMessage, Collections.singletonList(testError), 0, false, TestResult.Confidence.HIGH, testInfo); + } + + public Result addWithRequestError(String originalMessage, TestResult.TestError testError, OriginalHttpRequest request, TestInfo testInfo) { + TestResult testResult = buildFailedTestResultWithOriginalMessage(originalMessage,testError,request, testInfo); + List testResults = new ArrayList<>(); + testResults.add(testResult); + return new Result(testResults, false,new ArrayList<>(), 0); + } + + public TestResult buildTestResult(OriginalHttpRequest request, OriginalHttpResponse response, String originalMessage, + double percentageMatch, boolean isVulnerable, TestInfo testInfo) { + + List errors = new ArrayList<>(); + String message = null; + try { + message = RedactSampleData.convertOriginalReqRespToString(request, response); + } catch (Exception e) { + logger.error("Error while converting OriginalHttpRequest to string", e); + message = RedactSampleData.convertOriginalReqRespToString(new OriginalHttpRequest(), new OriginalHttpResponse()); + errors.add(TestResult.TestError.FAILED_TO_CONVERT_TEST_REQUEST_TO_STRING); + } + + return new TestResult(message, originalMessage, errors, percentageMatch, isVulnerable, TestResult.Confidence.HIGH, testInfo); + + } + + public Result addTestSuccessResult(boolean vulnerable, List testResults , List singleTypeInfos, TestResult.Confidence confidence) { + int confidencePercentage = confidence.equals(TestResult.Confidence.HIGH) ? 100 : 10; + return new Result(testResults, vulnerable,singleTypeInfos, confidencePercentage); + } + + public static class ContainsPrivateResourceResult { + boolean isPrivate; + List singleTypeInfos; + + public ContainsPrivateResourceResult(boolean isPrivate, List singleTypeInfos) { + this.isPrivate = isPrivate; + this.singleTypeInfos = singleTypeInfos; + } + + public List findPrivateOnes() { + List res = new ArrayList<>(); + for (SingleTypeInfo singleTypeInfo: singleTypeInfos) { + if (singleTypeInfo.getIsPrivate()) res.add(singleTypeInfo); + } + return res; + } + + public Set findPrivateParams() { + Set privateParams = new HashSet<>(); + for (SingleTypeInfo privateSTI: findPrivateOnes()) { + privateParams.add(privateSTI.getParam()); + } + + return privateParams; + } + } + + public static SingleTypeInfo findSti(String param, boolean isUrlParam, + ApiInfo.ApiInfoKey apiInfoKey, boolean isHeader, int responseCode, + Map singleTypeInfoMap) { + + String key = SingleTypeInfo.composeKey( + apiInfoKey.url, apiInfoKey.method.name(), responseCode, isHeader, + param,SingleTypeInfo.GENERIC, apiInfoKey.getApiCollectionId(), isUrlParam + ); + + SingleTypeInfo singleTypeInfo = singleTypeInfoMap.get(key); + + if (singleTypeInfo == null) return null; + + return singleTypeInfo.copy(); + } + + public void asdf(OriginalHttpRequest originalHttpRequest) { + String urlWithParams = originalHttpRequest.getFullUrlWithParams(); + BasicDBObject payload = RequestTemplate.parseRequestPayload(originalHttpRequest.getJsonRequestBody(), urlWithParams); + } + + public ContainsPrivateResourceResult containsPrivateResource(OriginalHttpRequest originalHttpRequest, ApiInfo.ApiInfoKey apiInfoKey, Map singleTypeInfoMap) { + String urlWithParams = originalHttpRequest.getFullUrlWithParams(); + String url = apiInfoKey.url; + URLMethods.Method method = apiInfoKey.getMethod(); + List singleTypeInfoList = new ArrayList<>(); + + boolean isPrivate = true; + boolean atLeastOneValueInRequest = false; + // check private resource in + // 1. url + if (APICatalog.isTemplateUrl(url)) { + URLTemplate urlTemplate = APICatalogSync.createUrlTemplate(url, method); + String[] tokens = urlTemplate.getTokens(); + String[] ogTokens = trimAndSplit(url); + for (int i = 0;i < tokens.length; i++) { + if (tokens[i] == null) { + atLeastOneValueInRequest = true; + SingleTypeInfo singleTypeInfo = findSti(i+"", true,apiInfoKey, false, -1, singleTypeInfoMap); + if (singleTypeInfo != null) { + String v = ogTokens[i]; + Set values = new HashSet<>(); + values.add(v); + singleTypeInfo.setValues(new CappedSet<>(values)); + singleTypeInfoList.add(singleTypeInfo); + isPrivate = isPrivate && singleTypeInfo.getIsPrivate(); + } + } + } + } + + // 2. payload + BasicDBObject payload = RequestTemplate.parseRequestPayload(originalHttpRequest.getJsonRequestBody(), urlWithParams); + Map> flattened = JSONUtils.flatten(payload); + for (String param: flattened.keySet()) { + atLeastOneValueInRequest = true; + SingleTypeInfo singleTypeInfo = findSti(param,false,apiInfoKey, false, -1, singleTypeInfoMap); + if (singleTypeInfo != null) { + Set valSet = flattened.get(param); + Set valStringSet = new HashSet<>(); + for (Object v: valSet) { + if (v == null) { + continue; + } + valStringSet.add(v.toString()); + } + singleTypeInfo.setValues(new CappedSet<>(valStringSet)); + singleTypeInfoList.add(singleTypeInfo); + isPrivate = isPrivate && singleTypeInfo.getIsPrivate(); + } + } + + // For private at least one value in request + boolean finalPrivateResult = isPrivate && atLeastOneValueInRequest; + + return new ContainsPrivateResourceResult(finalPrivateResult, singleTypeInfoList); + } + + public static List findUndocumentedMethods(Map> sampleMessages, ApiInfo.ApiInfoKey apiInfoKey) { + // We will hit only those methods whose traffic doesn't exist. For that we see if corresponding method exists or not in sample messages + List undocumentedMethods = new ArrayList<>(); + List methodList = Arrays.asList( + URLMethods.Method.GET, URLMethods.Method.POST, URLMethods.Method.PUT, URLMethods.Method.DELETE, + URLMethods.Method.PATCH + ); + + String apiUrl = apiInfoKey.getUrl(); + URLMethods.Method apiMethod = apiInfoKey.getMethod(); + + + for (URLMethods.Method fakeMethod: methodList) { + ApiInfo.ApiInfoKey methodApiInfoKey = new ApiInfo.ApiInfoKey(apiInfoKey.getApiCollectionId(), apiUrl, fakeMethod); + if (sampleMessages.containsKey(methodApiInfoKey)) continue; + + boolean found = false; + for (ApiInfoKey apiInfoKeyFromDb: sampleMessages.keySet()) { + String apiFromDbUrl = apiInfoKeyFromDb.getUrl(); + URLMethods.Method apiFromDbMethod = apiInfoKeyFromDb.getMethod(); + if (apiInfoKeyFromDb.getApiCollectionId() != apiInfoKey.getApiCollectionId()) continue; + + if (!APICatalog.isTemplateUrl(apiFromDbUrl)) continue; + + URLTemplate urlTemplate = APICatalogSync.createUrlTemplate(apiFromDbUrl, apiFromDbMethod); + + found = urlTemplate.match(apiUrl, fakeMethod); + + if (found) break; + } + + if (found) continue; + + undocumentedMethods.add(fakeMethod); + } + + return undocumentedMethods; + } + + public ApiExecutionDetails executeApiAndReturnDetails(OriginalHttpRequest testRequest, boolean followRedirects, RawApi rawApi) throws Exception { + OriginalHttpResponse testResponse = ApiExecutor.sendRequest(testRequest, followRedirects);; + + OriginalHttpRequest originalHttpRequest = rawApi.getRequest().copy(); + OriginalHttpResponse originalHttpResponse = rawApi.getResponse().copy(); + + int statusCode = StatusCodeAnalyser.getStatusCode(testResponse.getBody(), testResponse.getStatusCode()); + PercentageMatchRequest percentMatchReq = getPercentageMatchRequest(originalHttpRequest, 2, followRedirects, originalHttpResponse); + double percentageMatch = compareWithOriginalResponse(percentMatchReq.getResponse().getBody(), testResponse.getBody(), percentMatchReq.getExcludedKeys()); + + String originalMessage = rawApi.getOriginalMessage(); + + Map json = gson.fromJson(originalMessage, Map.class); + if (percentMatchReq.getResponse() != null) { + json.put("responsePayload", percentMatchReq.getResponse().getBody()); + try { + JSONObject headers = new JSONObject(); + for (String headerName: percentMatchReq.getResponse().getHeaders().keySet()) { + List headerValues = percentMatchReq.getResponse().getHeaders().get(headerName); + String val = String.join(";", headerValues); + headers.put(headerName, val); + } + String responseHeaders = headers.toString(); + json.put("responseHeaders", responseHeaders); + } catch (Exception e) { + logger.error("response exracting response header from percent match req"); + } + originalMessage = gson.toJson(json); + rawApi.setOriginalMessage(originalMessage); + } + + return new ApiExecutionDetails(statusCode, percentageMatch, testResponse, percentMatchReq.getResponse(), originalMessage); + } + + private PercentageMatchRequest getPercentageMatchRequest(OriginalHttpRequest request, int replayCount, boolean followRedirects, OriginalHttpResponse originalHttpResponse) throws Exception { + + Map comparisonExcludedKeys = new HashMap<>(); + PercentageMatchRequest percentMatchReq = new PercentageMatchRequest(originalHttpResponse, comparisonExcludedKeys); + + SampleRequestReplayResponse sampleReplayResp = replaySampleReq(request, replayCount, followRedirects, originalHttpResponse); + ArrayList replayedResponses = sampleReplayResp.getReplayedResponses(); + if (replayedResponses.size() < 2) { + return percentMatchReq; + } + + comparisonExcludedKeys = getComparisonExcludedKeys(sampleReplayResp, sampleReplayResp.getReplayedResponseMap()); + percentMatchReq.setExcludedKeys(comparisonExcludedKeys); + percentMatchReq.setResponse(replayedResponses.get(replayedResponses.size() - 1)); + + return percentMatchReq; + } + + public static Map getComparisonExcludedKeys(SampleRequestReplayResponse sampleReplayResp, ArrayList>> replayedResponseMap) { + + Map comparisonExcludedKeys = new HashMap<>(); + Set keys = new HashSet<>(); + for (String k1: replayedResponseMap.get(0).keySet()) { + keys.add(k1); + } + + for (String key : keys) { + Set> data = new HashSet<>(); + for (int i = 0; i < replayedResponseMap.size(); i++) { + Set v1 = replayedResponseMap.get(i).get(key); + if (v1 == null) { + break; + } + data.add(v1); + } + if (data.size() > 1) { + comparisonExcludedKeys.put(key, true); + } + } + + return comparisonExcludedKeys; + } + + private SampleRequestReplayResponse replaySampleReq(OriginalHttpRequest testRequest, int replayCount, boolean followRedirects, OriginalHttpResponse originalHttpResponse) throws Exception { + + OriginalHttpResponse replayedResponse; + ArrayList replayedResponses = new ArrayList<>(); + ArrayList>> replayedResponseMap = new ArrayList<>(); + + SampleRequestReplayResponse sampleReplayResp = new SampleRequestReplayResponse(); + + for (int i = 0; i < replayCount; i++) { + try { + replayedResponse = ApiExecutor.sendRequest(testRequest, followRedirects); + } catch (Exception e) { + logger.error("request replay failed with error " + e.getMessage()); + continue; + } + int replayedStatusCode = StatusCodeAnalyser.getStatusCode(replayedResponse.getBody(), replayedResponse.getStatusCode()); + int originalStatusCode = StatusCodeAnalyser.getStatusCode(originalHttpResponse.getBody(), originalHttpResponse.getStatusCode()); + if (replayedStatusCode == originalStatusCode) { + try { + Map> originalResponseParamMap = new HashMap<>(); + extractAllValuesFromPayload(replayedResponse.getBody(), originalResponseParamMap); + replayedResponseMap.add(originalResponseParamMap); + replayedResponses.add(replayedResponse); + } catch (Exception e) { + continue; + } + } + } + + sampleReplayResp.setReplayedResponseMap(replayedResponseMap); + sampleReplayResp.setReplayedResponses(replayedResponses); + return sampleReplayResp; + } + + public static class ApiExecutionDetails { + public int statusCode; + public double percentageMatch; + public OriginalHttpResponse testResponse; + public OriginalHttpResponse baseResponse; + public String originalReqResp; + + public ApiExecutionDetails(int statusCode, double percentageMatch, OriginalHttpResponse testResponse, OriginalHttpResponse baseResponse, String originalReqResp) { + this.statusCode = statusCode; + this.percentageMatch = percentageMatch; + this.testResponse = testResponse; + this.baseResponse = baseResponse; + this.originalReqResp = originalReqResp; + } + } + + public static class ExecutorResult { + boolean vulnerable; + TestResult.Confidence confidence; + List singleTypeInfos; + double percentageMatch; + RawApi rawApi; + OriginalHttpResponse testResponse; + OriginalHttpRequest testRequest; + + TestResult.TestError testError; + TestInfo testInfo; + + public ExecutorResult(boolean vulnerable, TestResult.Confidence confidence, List singleTypeInfos, + double percentageMatch, RawApi rawApi, TestResult.TestError testError, + OriginalHttpRequest testRequest, OriginalHttpResponse testResponse, TestInfo testInfo) { + this.vulnerable = vulnerable; + this.confidence = confidence; + this.singleTypeInfos = singleTypeInfos; + this.percentageMatch = percentageMatch; + this.rawApi = rawApi; + this.testError = testError; + this.testRequest = testRequest; + this.testResponse = testResponse; + this.testInfo = testInfo; + } + } + + public static class Result { + public List testResults; + public boolean isVulnerable; + public List singleTypeInfos; + public int confidencePercentage; + + public Result(List testResults, boolean isVulnerable, List singleTypeInfos, int confidencePercentage) { + this.testResults = testResults; + this.isVulnerable = isVulnerable; + this.singleTypeInfos = singleTypeInfos; + this.confidencePercentage = confidencePercentage; + } + } + + + + public Result convertExecutorResultsToResult(List results) { + + if (results.isEmpty()) return null; + + boolean vulnerable = false; + + List testResults = new ArrayList<>(); + for (BOLATest.ExecutorResult result: results) { + vulnerable = vulnerable || result.vulnerable; + TestResult testResult; + if (result.testError == null) { + testResult = buildTestResult( + result.testRequest, result.testResponse, result.rawApi.getOriginalMessage(), + result.percentageMatch, result.vulnerable, result.testInfo + ); + } else { + testResult = buildFailedTestResultWithOriginalMessage(result.rawApi.getOriginalMessage(), result.testError, result.testRequest, result.testInfo); + } + testResults.add(testResult); + } + + + return addTestSuccessResult( + vulnerable, testResults, results.get(0).singleTypeInfos, TestResult.Confidence.HIGH + ); + } + + + public static class TestRoleMatcher { + List friends; + List enemies; + + public TestRoleMatcher(List testRolesList, ApiInfo.ApiInfoKey apiInfoKey) { + this.friends = new ArrayList<>(); + this.enemies = new ArrayList<>(); + + for (TestRoles testRoles: testRolesList) { + EndpointLogicalGroup endpointLogicalGroup = testRoles.fetchEndpointLogicalGroup(); + if (endpointLogicalGroup == null) continue; + TestingEndpoints testingEndpoints = endpointLogicalGroup.getTestingEndpoints(); + if (testingEndpoints == null) continue; + if (testingEndpoints.containsApi(apiInfoKey) ) { + this.friends.add(testRoles); + } else { + this.enemies.add(testRoles); + } + } + } + + + public boolean shouldDoBFLA() { + return this.friends.size() > 0; + } + } + +} diff --git a/apps/testing/src/main/java/com/akto/store/SampleMessageStore.java b/apps/testing/src/main/java/com/akto/store/SampleMessageStore.java new file mode 100644 index 0000000000..893c58d70f --- /dev/null +++ b/apps/testing/src/main/java/com/akto/store/SampleMessageStore.java @@ -0,0 +1,122 @@ +package com.akto.store; + +import com.akto.dao.SampleDataDao; +import com.akto.dao.testing.EndpointLogicalGroupDao; +import com.akto.dao.testing.TestRolesDao; +import com.akto.dto.*; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.HttpRequestParams; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.ApiInfo.ApiInfoKey; +import com.akto.dto.testing.*; +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.SingleTypeInfo; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +public class SampleMessageStore { + + + private static final Logger logger = LoggerFactory.getLogger(SampleMessageStore.class); + public static Map buildSingleTypeInfoMap(TestingEndpoints testingEndpoints) { + Map singleTypeInfoMap = new HashMap<>(); + if (testingEndpoints == null) return singleTypeInfoMap; + TestingEndpoints.Type type = testingEndpoints.getType(); + List singleTypeInfoList = new ArrayList<>(); + try { + if (type.equals(TestingEndpoints.Type.COLLECTION_WISE)) { + CollectionWiseTestingEndpoints collectionWiseTestingEndpoints = (CollectionWiseTestingEndpoints) testingEndpoints; + int apiCollectionId = collectionWiseTestingEndpoints.getApiCollectionId(); + singleTypeInfoList = SingleTypeInfoDao.instance.findAll( + Filters.and( + Filters.eq(SingleTypeInfo._API_COLLECTION_ID, apiCollectionId), + Filters.eq(SingleTypeInfo._RESPONSE_CODE, -1), + Filters.eq(SingleTypeInfo._IS_HEADER, false) + ) + ); + } else { + CustomTestingEndpoints customTestingEndpoints = (CustomTestingEndpoints) testingEndpoints; + List apiInfoKeys = customTestingEndpoints.getApisList(); + + if (apiInfoKeys.size() == 0) { + return singleTypeInfoMap; + } else { + int apiCollectionId = apiInfoKeys.get(0).getApiCollectionId(); + singleTypeInfoList = SingleTypeInfoDao.instance.findAll( + Filters.and( + Filters.eq(SingleTypeInfo._API_COLLECTION_ID, apiCollectionId), + Filters.eq(SingleTypeInfo._RESPONSE_CODE, -1), + Filters.eq(SingleTypeInfo._IS_HEADER, false) + ) + ); + } + } + + for (SingleTypeInfo singleTypeInfo: singleTypeInfoList) { + singleTypeInfoMap.put(singleTypeInfo.composeKeyWithCustomSubType(SingleTypeInfo.GENERIC), singleTypeInfo); + } + } catch (Exception e) { + ; + } + + return singleTypeInfoMap; + } + + public static List fetchTestRoles() { + return TestRolesDao.instance.findAll(new BasicDBObject()); + } + + + public static Map> fetchSampleMessages() { + List sampleDataList = SampleDataDao.instance.findAll(new BasicDBObject(), 0, 10_000, null); + Map> tempSampleDataMap = new HashMap<>(); + for (SampleData sampleData: sampleDataList) { + if (sampleData.getSamples() == null) continue; + Key key = sampleData.getId(); + ApiInfo.ApiInfoKey apiInfoKey = new ApiInfo.ApiInfoKey(key.getApiCollectionId(), key.getUrl(), key.getMethod()); + if (tempSampleDataMap.containsKey(apiInfoKey)) { + tempSampleDataMap.get(apiInfoKey).addAll(sampleData.getSamples()); + } else { + tempSampleDataMap.put(apiInfoKey, sampleData.getSamples()); + } + } + + return new HashMap<>(tempSampleDataMap); + } + + + + public static List fetchAllOriginalMessages(ApiInfoKey apiInfoKey, Map> sampleMessages) { + List messages = new ArrayList<>(); + + List samples = sampleMessages.get(apiInfoKey); + if (samples == null || samples.isEmpty()) return messages; + + for (String message: samples) { + try { + messages.add(RawApi.buildFromMessage(message)); + } catch(Exception ignored) { } + + } + + return messages; + } + + public static List filterMessagesWithAuthToken(List messages, AuthMechanism authMechanism) { + List filteredMessages = new ArrayList<>(); + for (RawApi rawApi: messages) { + OriginalHttpRequest request = rawApi.getRequest(); + boolean containsAuthToken = authMechanism.authTokenPresent(request); + if (containsAuthToken) filteredMessages.add(rawApi); + } + + return filteredMessages; + } + +} diff --git a/apps/testing/src/main/java/com/akto/store/TestingUtil.java b/apps/testing/src/main/java/com/akto/store/TestingUtil.java new file mode 100644 index 0000000000..7bf732e9ae --- /dev/null +++ b/apps/testing/src/main/java/com/akto/store/TestingUtil.java @@ -0,0 +1,61 @@ +package com.akto.store; + +import com.akto.dto.ApiInfo; +import com.akto.dto.testing.AuthMechanism; +import com.akto.dto.testing.TestRoles; +import com.akto.dto.type.SingleTypeInfo; + +import java.util.List; +import java.util.Map; + +public class TestingUtil { + + private AuthMechanism authMechanism; + private java.util.Map> sampleMessages; + private Map singleTypeInfoMap; + + private List testRoles; + + public TestingUtil(AuthMechanism authMechanism, Map> sampleMessages, + Map singleTypeInfoMap, List testRoles) { + this.authMechanism = authMechanism; + this.sampleMessages = sampleMessages; + this.singleTypeInfoMap = singleTypeInfoMap; + this.testRoles = testRoles; + } + + public TestingUtil() { + } + + public AuthMechanism getAuthMechanism() { + return authMechanism; + } + + public void setAuthMechanism(AuthMechanism authMechanism) { + this.authMechanism = authMechanism; + } + + public Map> getSampleMessages() { + return sampleMessages; + } + + public void setSampleMessages(Map> sampleMessages) { + this.sampleMessages = sampleMessages; + } + + public Map getSingleTypeInfoMap() { + return singleTypeInfoMap; + } + + public void setSingleTypeInfoMap(Map singleTypeInfoMap) { + this.singleTypeInfoMap = singleTypeInfoMap; + } + + public List getTestRoles() { + return testRoles; + } + + public void setTestRoles(List testRoles) { + this.testRoles = testRoles; + } +} diff --git a/apps/testing/src/main/java/com/akto/testing/ApiExecutor.java b/apps/testing/src/main/java/com/akto/testing/ApiExecutor.java new file mode 100644 index 0000000000..d771180b7f --- /dev/null +++ b/apps/testing/src/main/java/com/akto/testing/ApiExecutor.java @@ -0,0 +1,139 @@ +package com.akto.testing; + +import com.akto.dto.AccountSettings; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.dto.type.URLMethods; +import kotlin.Pair; +import okhttp3.*; + +import java.io.IOException; +import java.util.*; + +public class ApiExecutor { + public static OriginalHttpResponse common(Request request, boolean followRedirects) throws Exception { + + OkHttpClient client = HTTPClientHandler.instance.getHTTPClient(followRedirects); + + Call call = client.newCall(request); + Response response = null; + String body; + try { + response = call.execute(); + ResponseBody responseBody = response.body(); + if (responseBody == null) { + throw new Exception("Couldn't read response body"); + } + try { + body = responseBody.string(); + } catch (IOException e) { + body = "{}"; + } + } catch (IOException e) { + throw new Exception("Api Call failed"); + } finally { + if (response != null) { + response.close(); + } + } + + int statusCode = response.code(); + Headers headers = response.headers(); + Iterator> headersIterator = headers.iterator(); + Map> responseHeaders = new HashMap<>(); + while (headersIterator.hasNext()) { + Pair v = headersIterator.next(); + String headerKey = v.getFirst(); + if (!responseHeaders.containsKey(headerKey)) { + responseHeaders.put(headerKey, new ArrayList<>()); + } + String headerValue = v.getSecond(); + responseHeaders.get(headerKey).add(headerValue); + } + + return new OriginalHttpResponse(body, responseHeaders, statusCode); + } + + + public static OriginalHttpResponse sendRequest(OriginalHttpRequest request, boolean followRedirects) throws Exception { + // don't lowercase url because query params will change and will result in incorrect request + String url = request.getUrl(); + url = url.trim(); + if (!url.startsWith("http")) { + url = OriginalHttpRequest.makeUrlAbsolute(url, request.findHostFromHeader(), request.findProtocolFromHeader()); + } + + request.setUrl(url); + + Request.Builder builder = new Request.Builder(); + + // add headers + List forbiddenHeaders = Arrays.asList("content-length", "accept-encoding"); + Map> headersMap = request.getHeaders(); + if (headersMap == null) headersMap = new HashMap<>(); + headersMap.put(AccountSettings.AKTO_IGNORE_FLAG, Collections.singletonList("0")); + for (String headerName: headersMap.keySet()) { + if (forbiddenHeaders.contains(headerName)) continue; + List headerValueList = headersMap.get(headerName); + if (headerValueList == null || headerValueList.isEmpty()) continue; + for (String headerValue: headerValueList) { + if (headerValue == null) continue; + builder.addHeader(headerName, headerValue); + } + } + + URLMethods.Method method = URLMethods.Method.fromString(request.getMethod()); + + builder = builder.url(request.getFullUrlWithParams()); + + OriginalHttpResponse response = null; + switch (method) { + case GET: + case HEAD: + response = getRequest(request, builder, followRedirects); + break; + case POST: + case PUT: + case DELETE: + case OPTIONS: + case PATCH: + case TRACE: + response = sendWithRequestBody(request, builder, followRedirects); + break; + case OTHER: + throw new Exception("Invalid method name"); + } + + return response; + } + + + public static OriginalHttpResponse getRequest(OriginalHttpRequest request, Request.Builder builder, boolean followRedirects) throws Exception{ + Request okHttpRequest = builder.build(); + return common(okHttpRequest, followRedirects); + } + + + + public static OriginalHttpResponse sendWithRequestBody(OriginalHttpRequest request, Request.Builder builder, boolean followRedirects) throws Exception { + Map> headers = request.getHeaders(); + if (headers == null) { + headers = new HashMap<>(); + request.setHeaders(headers); + } + String contentType = request.findContentType(); + String payload = request.getBody(); + if (contentType == null ) { + contentType = "application/json; charset=utf-8"; + if (payload == null) payload = "{}"; + payload = payload.trim(); + if (!payload.startsWith("[") && !payload.startsWith("{")) payload = "{}"; + } + + if (payload == null) payload = ""; + RequestBody body = RequestBody.create(payload, MediaType.parse(contentType)); + builder = builder.method(request.getMethod(), body); + Request okHttpRequest = builder.build(); + return common(okHttpRequest, followRedirects); + } +} diff --git a/apps/testing/src/main/java/com/akto/testing/ApiWorkflowExecutor.java b/apps/testing/src/main/java/com/akto/testing/ApiWorkflowExecutor.java new file mode 100644 index 0000000000..e1dfff76cc --- /dev/null +++ b/apps/testing/src/main/java/com/akto/testing/ApiWorkflowExecutor.java @@ -0,0 +1,650 @@ +package com.akto.testing; + +import com.akto.DaoInit; +import com.akto.dao.OtpTestDataDao; +import com.akto.dao.RecordedLoginInputDao; +import com.akto.dao.context.Context; +import com.akto.dao.testing.LoginFlowStepsDao; +import com.akto.dao.testing.TestingRunDao; +import com.akto.dao.testing.WorkflowTestResultsDao; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.dto.RecordedLoginFlowInput; +import com.akto.dto.api_workflow.Graph; +import com.akto.dto.api_workflow.Node; +import com.akto.dto.testing.*; +import com.akto.dto.type.RequestTemplate; +import com.akto.util.JSONUtils; +import com.akto.utils.RedactSampleData; +import com.google.gson.Gson; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.akto.util.RecordedLoginFlowUtil; + +import org.apache.commons.lang3.StringUtils; +import org.apache.kafka.common.protocol.types.Field.Str; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +import java.io.File; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ApiWorkflowExecutor { + + private static final Logger logger = LoggerFactory.getLogger(ApiWorkflowExecutor.class); + private static final Gson gson = new Gson(); + + public void init(WorkflowTest workflowTest, ObjectId testingRunId, ObjectId testingRunSummaryId) { + Graph graph = new Graph(); + graph.buildGraph(workflowTest); + + List nodes = graph.sort(); + Map valuesMap = new HashMap<>(); + + int id = Context.now(); + WorkflowTestResult workflowTestResult = new WorkflowTestResult(id, workflowTest.getId(), new HashMap<>(), testingRunId, testingRunSummaryId); + Map testResultMap = workflowTestResult.getNodeResultMap(); + for (Node node: nodes) { + WorkflowTestResult.NodeResult nodeResult; + try { + nodeResult = processNode(node, valuesMap, true); + } catch (Exception e) { + ; + List testErrors = new ArrayList<>(); + testErrors.add("Something went wrong"); + nodeResult = new WorkflowTestResult.NodeResult("{}", false, testErrors); + } + + testResultMap.put(node.getId(), nodeResult); + + if (nodeResult.getErrors().size() > 0) break; + } + + WorkflowTestResultsDao.instance.insertOne(workflowTestResult); + } + + public LoginFlowResponse runLoginFlow(WorkflowTest workflowTest, AuthMechanism authMechanism, LoginFlowParams loginFlowParams) throws Exception { + Graph graph = new Graph(); + graph.buildGraph(workflowTest); + + ArrayList responses = new ArrayList(); + + List nodes = graph.sort(); + + if (loginFlowParams != null && loginFlowParams.getFetchValueMap()) { + nodes.get(0).setId(loginFlowParams.getNodeId()); + } + + Map valuesMap = constructValueMap(loginFlowParams); + + for (Node node: nodes) { + WorkflowTestResult.NodeResult nodeResult; + try { + nodeResult = processNode(node, valuesMap, false); + } catch (Exception e) { + ; + List testErrors = new ArrayList<>(); + testErrors.add("Error Processing Node In Login Flow " + e.getMessage()); + nodeResult = new WorkflowTestResult.NodeResult("{}", false, testErrors); + } + + JSONObject respString = new JSONObject(); + Map> json = gson.fromJson(nodeResult.getMessage(), Map.class); + + respString.put("headers", json.get("response").get("headers")); + respString.put("body", json.get("response").get("body")); + responses.add(respString.toString()); + + if (nodeResult.getErrors().size() > 0) { + return new LoginFlowResponse(responses, "Failed to process node " + node.getId(), false); + } else { + if (loginFlowParams != null && loginFlowParams.getFetchValueMap()) { + saveValueMapData(loginFlowParams, valuesMap); + } + } + + } + + for (AuthParam param : authMechanism.getAuthParams()) { + try { + String value = executeCode(param.getValue(), valuesMap); + if (!param.getValue().equals(value) && value == null) { + return new LoginFlowResponse(responses, "auth param not found at specified path " + + param.getValue(), false); + } + param.setValue(value); + } catch(Exception e) { + return new LoginFlowResponse(responses, "error resolving auth param " + param.getValue(), false); + } + } + return new LoginFlowResponse(responses, null, true); + } + + public Map constructValueMap(LoginFlowParams loginFlowParams) { + Map valuesMap = new HashMap<>(); + if (loginFlowParams == null || !loginFlowParams.getFetchValueMap()) { + return valuesMap; + } + Bson filters = Filters.and( + Filters.eq("userId", loginFlowParams.getUserId()) + ); + LoginFlowStepsData loginFlowStepData = LoginFlowStepsDao.instance.findOne(filters); + + if (loginFlowStepData == null || loginFlowStepData.getValuesMap() == null) { + return valuesMap; + } + + valuesMap = loginFlowStepData.getValuesMap(); + + Set keysToRemove = new HashSet(); + for(String key : valuesMap.keySet()){ + if(key.startsWith(loginFlowParams.getNodeId())){ + keysToRemove.add(key); + } + } + + valuesMap.keySet().removeAll(keysToRemove); + + return valuesMap; + } + + public Map saveValueMapData(LoginFlowParams loginFlowParams, Map valuesMap) { + + Integer userId = loginFlowParams.getUserId(); + + Bson filter = Filters.and( + Filters.eq("userId", loginFlowParams.getUserId()) + ); + Bson update = Updates.set("valuesMap", valuesMap); + LoginFlowStepsDao.instance.updateOne(filter, update); + return valuesMap; + } + + public WorkflowTestResult.NodeResult processOtpNode(Node node, Map valuesMap) { + + List testErrors = new ArrayList<>(); + BasicDBObject resp = new BasicDBObject(); + BasicDBObject body = new BasicDBObject(); + BasicDBObject data = new BasicDBObject(); + String message; + + OtpTestData otpTestData = fetchOtpTestData(node, 4); + String uuid = node.getWorkflowNodeDetails().getOtpRefUuid(); + + if (otpTestData == null) { + message = "otp data not received for uuid " + uuid; + data.put("error", message); + body.put("body", data); + resp.put("response", body); + testErrors.add(message); + return new WorkflowTestResult.NodeResult(resp.toString(), false, testErrors); + } + try { + String otp = extractOtpCode(otpTestData.getOtpText(), node.getWorkflowNodeDetails().getOtpRegex()); + if (otp == null) { + data.put("error", "unable to extract otp for provided regex"); + testErrors.add("unable to extract otp for provided regex"); + } else { + data.put("otp", otp); + data.put("otpText", otpTestData.getOtpText()); + } + body.put("body", data); + resp.put("response", body); + valuesMap.put(node.getId() + ".response.body.otp", otp); + } catch(Exception e) { + message ="Error extracting otp data for uuid " + uuid + " error " + e.getMessage(); + data.put("error", message); + body.put("body", data); + resp.put("response", body); + testErrors.add(message); + return new WorkflowTestResult.NodeResult(resp.toString(), false, testErrors); + } + return new WorkflowTestResult.NodeResult(resp.toString(), false, testErrors); + } + + private OtpTestData fetchOtpTestData(Node node, int retries) { + OtpTestData otpTestData = null; + for (int i=0; i 0) { + logger.info("WAITING: " + waitInSeconds + " seconds"); + Thread.sleep(waitInSeconds*1000); + logger.info("DONE WAITING!!!!"); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + String uuid = node.getWorkflowNodeDetails().getOtpRefUuid(); + int curTime = Context.now() - 5 * 60; + Bson filters = Filters.and( + Filters.eq("uuid", uuid), + Filters.gte("createdAtEpoch", curTime) + ); + otpTestData = OtpTestDataDao.instance.findOne(filters); + if (otpTestData != null) { + break; + } + } + return otpTestData; + } + + private String extractOtpCode(String text, String regex) { + logger.info(regex); + + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(text); + String verificationCode = null; + if (matcher.find()) { + verificationCode = matcher.group(1); + } + + return verificationCode; + } + + public WorkflowTestResult.NodeResult processRecorderNode(Node node, Map valuesMap) { + + List testErrors = new ArrayList<>(); + BasicDBObject resp = new BasicDBObject(); + BasicDBObject body = new BasicDBObject(); + BasicDBObject data = new BasicDBObject(); + String message; + + RecordedLoginFlowInput recordedLoginFlowInput = RecordedLoginInputDao.instance.findOne(new BasicDBObject()); + + String token = fetchToken(recordedLoginFlowInput, 5); + + if (token == null){ + message = "error processing reorder node"; + data.put("error", message); + body.put("body", data); + resp.put("response", body); + testErrors.add(message); + return new WorkflowTestResult.NodeResult(resp.toString(), false, testErrors); + } + + valuesMap.put(node.getId() + ".response.body.token", token); + + data.put("token", token); + body.put("body", data); + resp.put("response", body); + return new WorkflowTestResult.NodeResult(resp.toString(), false, testErrors); + } + + private String fetchToken(RecordedLoginFlowInput recordedLoginFlowInput, int retries) { + + String token = null; + for (int i=0; i valuesMap, Boolean allowAllStatusCodes) { + if (node.getWorkflowNodeDetails().getType() == WorkflowNodeDetails.Type.RECORDED) { + return processRecorderNode(node, valuesMap); + } + else if (node.getWorkflowNodeDetails().getType() == WorkflowNodeDetails.Type.OTP) { + return processOtpNode(node, valuesMap); + } + else { + return processApiNode(node, valuesMap, allowAllStatusCodes); + } + } + + + public WorkflowTestResult.NodeResult processApiNode(Node node, Map valuesMap, Boolean allowAllStatusCodes) { + logger.info("\n"); + logger.info("NODE: " + node.getId()); + List testErrors = new ArrayList<>(); + String nodeId = node.getId(); + WorkflowNodeDetails workflowNodeDetails = node.getWorkflowNodeDetails(); + WorkflowUpdatedSampleData updatedSampleData = workflowNodeDetails.getUpdatedSampleData(); + WorkflowNodeDetails.Type type = workflowNodeDetails.getType(); + boolean followRedirects = !workflowNodeDetails.getOverrideRedirect(); + + OriginalHttpRequest request; + try { + request = buildHttpRequest(updatedSampleData, valuesMap); + if (request == null) throw new Exception(); + } catch (Exception e) { + ; + return new WorkflowTestResult.NodeResult(null, false, Collections.singletonList("Failed building request body")); + } + + String url = request.getUrl(); + valuesMap.put(nodeId + ".request.url", url); + + populateValuesMap(valuesMap, request.getBody(), nodeId, request.getHeaders(), + true, request.getQueryParams()); + + OriginalHttpResponse response = null; + int maxRetries = type.equals(WorkflowNodeDetails.Type.POLL) ? node.getWorkflowNodeDetails().getMaxPollRetries() : 1; + + try { + int waitInSeconds = Math.min(workflowNodeDetails.getWaitInSeconds(),60); + if (waitInSeconds > 0) { + logger.info("WAITING: " + waitInSeconds + " seconds"); + Thread.sleep(waitInSeconds*1000); + logger.info("DONE WAITING!!!!"); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + for (int i = 0; i < maxRetries; i++) { + try { + if (i > 0) { + int sleep = node.getWorkflowNodeDetails().getPollRetryDuration(); + logger.info("Waiting "+ (sleep/1000) +" before sending another request......"); + Thread.sleep(sleep); + } + + response = ApiExecutor.sendRequest(request, followRedirects); + + int statusCode = response.getStatusCode(); + + String statusKey = nodeId + "." + "response" + "." + "status_code"; + valuesMap.put(statusKey, statusCode); + + populateValuesMap(valuesMap, response.getBody(), nodeId, response.getHeaders(), false, null); + if (!allowAllStatusCodes && (statusCode >= 400)) { + testErrors.add("process node failed with status code " + statusCode); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + testErrors.add("API request failed"); + ; + } + } + + String message = null; + try { + message = RedactSampleData.convertOriginalReqRespToString(request, response); + } catch (Exception e) { + ; + } + + boolean vulnerable = validateTest(workflowNodeDetails.getTestValidatorCode(), valuesMap); + return new WorkflowTestResult.NodeResult(message,vulnerable, testErrors); + + } + + public boolean validateTest(String testValidatorCode, Map valuesMap) { + if (testValidatorCode == null) return false; + testValidatorCode = testValidatorCode.trim(); + + boolean vulnerable = false; + if (testValidatorCode.length() == 0) return false; + + ScriptEngine engine = factory.getEngineByName("nashorn"); + try { + String code = replaceVariables(testValidatorCode, valuesMap, true); + logger.info("*******************************************************************"); + logger.info("TEST VALIDATOR CODE:"); + logger.info(code); + Object o = engine.eval(code); + logger.info("TEST VALIDATOR RESULT: " + o.toString()); + logger.info("*******************************************************************"); + vulnerable = ! (boolean) o; + } catch (Exception e) { + ; + } + + return vulnerable; + } + + public void populateValuesMap(Map valuesMap, String payloadStr, String nodeId, Map> headers, boolean isRequest, String queryParams) { + boolean isList = false; + String reqOrResp = isRequest ? "request" : "response"; + + if (payloadStr == null) payloadStr = "{}"; + if (payloadStr.startsWith("[")) { + payloadStr = "{\"json\": "+payloadStr+"}"; + isList = true; + } + + String fullBodyKey = nodeId + "." + reqOrResp + "." + "body"; + + valuesMap.put(fullBodyKey, payloadStr); + + BasicDBObject payloadObj; + try { + payloadObj = BasicDBObject.parse(payloadStr); + } catch (Exception e) { + boolean isPostFormData = payloadStr.contains("&") && payloadStr.contains("="); + if (isPostFormData) { + String mockUrl = "url?"+ payloadStr; // because getQueryJSON function needs complete url + payloadObj = RequestTemplate.getQueryJSON(mockUrl); + } else { + payloadObj = BasicDBObject.parse("{}"); + } + } + + BasicDBObject queryParamsObject = null; + if (queryParams != null) { + try { + String mockUrl = "url?"+ queryParams; // because getQueryJSON function needs complete url + queryParamsObject = RequestTemplate.getQueryJSON(mockUrl); + } catch (Exception e) { + ; + } + } + + Object obj; + if (isList) { + obj = payloadObj.get("json"); + } else { + obj = payloadObj; + } + + BasicDBObject flattened = JSONUtils.flattenWithDots(obj); + + + for (String param: flattened.keySet()) { + String key = nodeId + "." + reqOrResp + "." + "body" + "." + param; + valuesMap.put(key, flattened.get(param)); + } + + if (queryParamsObject != null) { + BasicDBObject queryFlattened = JSONUtils.flattenWithDots(queryParamsObject); + for (String param: queryFlattened.keySet()) { + String key = nodeId + "." + reqOrResp + "." + "query" + "." + param; + valuesMap.put(key, queryFlattened.get(param)); + } + } + + for (String headerName: headers.keySet()) { + List headerValues = headers.get(headerName); + String key = nodeId + "." + reqOrResp + "." + "header" + "." + headerName; + + switch (headerValues.size()) { + case 0: + continue; + case 1: + valuesMap.put(key, headerValues.get(0)); + continue; + default: + String val = String.join(";", headers.get(headerName)); + valuesMap.put(key, val); + } + + + } + } + + + + + public OriginalHttpRequest buildHttpRequest(WorkflowUpdatedSampleData updatedSampleData, Map valuesMap) throws Exception { + + String sampleData = updatedSampleData.getOrig(); + OriginalHttpRequest request = new OriginalHttpRequest(); + request.buildFromSampleMessage(sampleData); + + String queryParams = updatedSampleData.getQueryParams(); + String requestHeaders = updatedSampleData.getRequestHeaders(); + String requestPayload = updatedSampleData.getRequestPayload(); + String requestUrl = updatedSampleData.getRequestUrl(); + + String queryFromReplacedUrl = null; + + boolean userSuppliedQueryParamsNullOrEmpty = queryParams == null || queryParams.trim().length() == 0; + if (requestUrl != null) { + logger.info("requestUrl: " + requestUrl); + String rawUrl = executeCode(requestUrl, valuesMap); + logger.info("rawUrl: " + requestUrl); + // this url might contain urlQueryParams. We need to move it queryParams + String[] rawUrlArr = rawUrl.split("\\?"); + request.setUrl(rawUrlArr[0]); + if (rawUrlArr.length > 1) { + queryFromReplacedUrl = rawUrlArr[1]; + } + logger.info("final url: " + request.getUrl()); + logger.info("queryFromReplacedUrl: " + queryFromReplacedUrl); + } + + if (userSuppliedQueryParamsNullOrEmpty) { + logger.info("setting null"); + request.setQueryParams(null); + } + + if (requestPayload != null) { + String finalPayload = executeCode(requestPayload, valuesMap); + request.setBody(finalPayload); + } + + if (requestHeaders != null) { + String finalPayload = executeCode(requestHeaders, valuesMap); + Map> res = OriginalHttpRequest.buildHeadersMap(finalPayload); + request.setHeaders(res); + } + + boolean queryFromReplacedUrlNullOrEmpty = queryFromReplacedUrl == null || queryFromReplacedUrl.trim().isEmpty(); + + if (!userSuppliedQueryParamsNullOrEmpty) { + logger.info("user has supplied query params"); + String finalQueryParams = executeCode(queryParams, valuesMap); + logger.info("finalQueryParams: " + finalQueryParams); + if (queryFromReplacedUrlNullOrEmpty) { + request.setQueryParams(finalQueryParams); + } else { + // combine original query params and user defined query params and latter overriding former + String combinedQueryParams = OriginalHttpRequest.combineQueryParams(queryFromReplacedUrl, finalQueryParams); + logger.info("combinedQueryParams: " + combinedQueryParams); + request.setQueryParams(combinedQueryParams); + } + } else if (!queryFromReplacedUrlNullOrEmpty) { + request.setQueryParams(queryFromReplacedUrl); + } + + return request; + } + + + private final ScriptEngineManager factory = new ScriptEngineManager(); + + public String executeCode(String ogPayload, Map valuesMap) throws Exception { + String variablesReplacedPayload = replaceVariables(ogPayload,valuesMap, true); + + String regex = "\\#\\[(.*?)]#"; + Pattern p = Pattern.compile(regex); + Matcher matcher = p.matcher(variablesReplacedPayload); + StringBuffer sb = new StringBuffer(); + + // create a Nashorn script engine + ScriptEngine engine = factory.getEngineByName("nashorn"); + + while (matcher.find()) { + String code = matcher.group(1); + code = code.trim(); + if (!code.endsWith(";")) code = code+";"; + try { + Object val = engine.eval(code); + matcher.appendReplacement(sb, val.toString()); + } catch (final ScriptException se) { + } + + } + + matcher.appendTail(sb); + return sb.toString(); + } + + + public String replaceVariables(String payload, Map valuesMap, boolean escapeString) throws Exception { + String regex = "\\$\\{(x\\d+\\.[\\w\\-\\[\\].]+|AKTO\\.changes_info\\..*?)\\}"; + Pattern p = Pattern.compile(regex); + + // replace with values + Matcher matcher = p.matcher(payload); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + String key = matcher.group(1); + if (key == null) continue; + Object obj = valuesMap.get(key); + if (obj == null) { + logger.error("couldn't find: " + key); + throw new Exception("Couldn't find " + key); + } + String val = obj.toString(); + if (escapeString) { + val = val.replace("\\", "\\\\") + .replace("\t", "\\t") + .replace("\b", "\\b") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\f", "\\f") + .replace("\'", "\\'") + .replace("\"", "\\\""); + } + matcher.appendReplacement(sb, ""); + sb.append(val); + } + + matcher.appendTail(sb); + + return sb.toString(); + } + + public static String generateKey(String nodeId, boolean isHeader, String param, boolean isRequest) { + return StringUtils.joinWith("@", nodeId, isHeader, param, isRequest); + } +} diff --git a/apps/testing/src/main/java/com/akto/testing/HTTPClientHandler.java b/apps/testing/src/main/java/com/akto/testing/HTTPClientHandler.java new file mode 100644 index 0000000000..ef22137ba6 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/testing/HTTPClientHandler.java @@ -0,0 +1,33 @@ +package com.akto.testing; + +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; + +import java.util.concurrent.TimeUnit; + +public class HTTPClientHandler { + private HTTPClientHandler() {} + + private final OkHttpClient clientWithoutFollowRedirect = new OkHttpClient().newBuilder() + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .connectionPool(new ConnectionPool(256, 5L, TimeUnit.MINUTES)) + .followRedirects(false) + .build(); + + private final OkHttpClient clientWithFollowRedirect = new OkHttpClient().newBuilder() + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .connectionPool(new ConnectionPool(256, 5L, TimeUnit.MINUTES)) + .followRedirects(true) + .build(); + + public static final HTTPClientHandler instance = new HTTPClientHandler(); + + public OkHttpClient getHTTPClient (boolean followRedirect) { + if (followRedirect) { + return clientWithFollowRedirect; + } + return clientWithoutFollowRedirect; + } +} diff --git a/apps/testing/src/main/java/com/akto/testing/Main.java b/apps/testing/src/main/java/com/akto/testing/Main.java new file mode 100644 index 0000000000..4dc8a8af47 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/testing/Main.java @@ -0,0 +1,121 @@ +package com.akto.testing; + +import com.akto.DaoInit; +import com.akto.dao.AccountSettingsDao; +import com.akto.dao.context.Context; +import com.akto.dao.testing.TestingRunConfigDao; +import com.akto.dao.testing.TestingRunDao; +import com.akto.dao.testing.TestingRunResultSummariesDao; +import com.akto.dto.AccountSettings; +import com.akto.dto.testing.TestingRun; +import com.akto.dto.testing.TestingRunConfig; +import com.akto.dto.testing.TestingRunResultSummary; +import com.akto.log.LoggerMaker; +import com.akto.util.Constants; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; + +import java.util.HashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +public class Main { + private static final LoggerMaker loggerMaker = new LoggerMaker(Main.class); + + public static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); + + public static void main(String[] args) throws InterruptedException { + String mongoURI = System.getenv("AKTO_MONGO_CONN");; + DaoInit.init(new ConnectionString(mongoURI)); + Context.accountId.set(1_000_000); + + int delta = Context.now() - 20*60; + + loggerMaker.infoAndAddToDb("Starting......."); + + AccountSettings accountSettings = AccountSettingsDao.instance.findOne(new BasicDBObject()); + boolean runStatusCodeAnalyser = accountSettings == null || + accountSettings.getSetupType() != AccountSettings.SetupType.PROD; + + if (runStatusCodeAnalyser) { + StatusCodeAnalyser.run(); + } + + TestExecutor testExecutor = new TestExecutor(); + + while (true) { + int start = Context.now(); + + Bson filter1 = Filters.and( + Filters.eq(TestingRun.STATE, TestingRun.State.SCHEDULED), + Filters.lte(TestingRun.SCHEDULE_TIMESTAMP, Context.now()) + ); + Bson filter2 = Filters.and( + Filters.eq(TestingRun.STATE, TestingRun.State.RUNNING), + Filters.lte(TestingRun.SCHEDULE_TIMESTAMP, delta) + ); + + Bson update = Updates.combine( + Updates.set(TestingRun.PICKED_UP_TIMESTAMP, Context.now()), + Updates.set(TestingRun.STATE, TestingRun.State.RUNNING) + ); + + TestingRun testingRun = TestingRunDao.instance.getMCollection().findOneAndUpdate( + Filters.or(filter1,filter2), update); + + + if (testingRun == null) { + try { + Thread.sleep(10 * 1000L); + continue; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + loggerMaker.infoAndAddToDb("Found one + " + testingRun.getId().toHexString()); + if (testingRun.getTestIdConfig() > 1) { + TestingRunConfig testingRunConfig = TestingRunConfigDao.instance.findOne(Constants.ID, testingRun.getTestIdConfig()); + if (testingRunConfig != null) { + loggerMaker.infoAndAddToDb("Found testing run config with id :" + testingRunConfig.getId()); + testingRun.setTestingRunConfig(testingRunConfig); + } + } + + TestingRunResultSummary summary = new TestingRunResultSummary(start, 0, new HashMap<>(), + 0, testingRun.getId(), testingRun.getId().toHexString(), 0); + + ObjectId summaryId = TestingRunResultSummariesDao.instance.insertOne(summary).getInsertedId().asObjectId().getValue(); + + try { + testExecutor.init(testingRun, summaryId); + } catch (Exception e) { + ; + } + + Bson completedUpdate = Updates.combine( + Updates.set(TestingRun.STATE, TestingRun.State.COMPLETED), + Updates.set(TestingRun.END_TIMESTAMP, Context.now()) + ); + + if (testingRun.getPeriodInSeconds() > 0 ) { + completedUpdate = Updates.combine( + Updates.set(TestingRun.STATE, TestingRun.State.SCHEDULED), + Updates.set(TestingRun.END_TIMESTAMP, Context.now()), + Updates.set(TestingRun.SCHEDULE_TIMESTAMP, testingRun.getScheduleTimestamp() + testingRun.getPeriodInSeconds()) + ); + } + + TestingRunDao.instance.getMCollection().findOneAndUpdate( + Filters.eq("_id", testingRun.getId()), completedUpdate + ); + + + loggerMaker.infoAndAddToDb("Tests completed in " + (Context.now() - start) + " seconds"); + } + } +} \ No newline at end of file diff --git a/apps/testing/src/main/java/com/akto/testing/NucleiExecutor.java b/apps/testing/src/main/java/com/akto/testing/NucleiExecutor.java new file mode 100644 index 0000000000..56a3c1068e --- /dev/null +++ b/apps/testing/src/main/java/com/akto/testing/NucleiExecutor.java @@ -0,0 +1,257 @@ +package com.akto.testing; + +import java.io.*; +import java.nio.charset.Charset; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import com.akto.dao.context.Context; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.rules.FuzzingTest; +import com.akto.util.Pair; + +import com.google.gson.Gson; +import com.mongodb.BasicDBObject; +import kotlin.text.Charsets; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; + +public class NucleiExecutor { + + public static class NucleiResult { + public ArrayList> attempts; + public List metaData; + + public NucleiResult(ArrayList> attempts, List metaData) { + this.attempts = attempts; + this.metaData = metaData; + } + } + + + public static NucleiResult execute( + String method, String baseURL, String templatePath, String outputDir, String body, Map> headers, + String pwd + ) { + + File outputDirFile = new File(outputDir); + if (!baseURL.endsWith("/")) { + baseURL += "/"; + } + + File file = new File( pwd+"/.templates-config.json"); + try { + FileUtils.writeStringToFile(file, "{}", Charsets.UTF_8); + } catch (IOException e) { + ; + return null; + } + + String outputDirCalls = outputDirFile.getAbsolutePath()+"/calls"; + String outputDirFiles = outputDirFile.getAbsolutePath()+"/files"; + + FuzzingTest.createDirPath(outputDirFiles+"/logs.txt"); + + String path = baseURL; + if (baseURL.contains("?")) path = baseURL.substring(0, baseURL.indexOf("?")); + String fullUrl = path.startsWith("http") ? path : "https://" + path; + + String arch = System.getProperty("os.arch"); + String nucleiFileSuffix = "linux"; + + if (arch != null && arch.equals("aarch64")) { + nucleiFileSuffix = "m1"; + } + + List baseCmdTokens = new ArrayList<>(); + baseCmdTokens.add("/app/nuclei_"+nucleiFileSuffix); + + baseCmdTokens.add("-u"); + baseCmdTokens.add(fullUrl); + + baseCmdTokens.add("-t"); + baseCmdTokens.add(templatePath); + + baseCmdTokens.add("-output-files-dir"); + baseCmdTokens.add(outputDirFiles); + + baseCmdTokens.add("-store-resp-dir"); + baseCmdTokens.add(outputDirCalls); + + baseCmdTokens.add("-template-dir"); + baseCmdTokens.add(pwd); + + baseCmdTokens.add("-v"); + baseCmdTokens.add("Method="+method); + + baseCmdTokens.add("-v"); + baseCmdTokens.add("Path="+path); + + if (StringUtils.isNotEmpty(body)) { + baseCmdTokens.add("-v"); + baseCmdTokens.add("Body=\"" + body.replaceAll("\"","\\\\\"") + "\""); + } + + for (String headerName: headers.keySet()) { + String headerValue = StringUtils.join(headers.get(headerName), ",").replaceAll("\"","\\\\\""); + baseCmdTokens.add("-h"); + baseCmdTokens.add(headerName + ":\"" + headerValue + "\""); + } + + Process process; + + try { + process = Runtime.getRuntime().exec(baseCmdTokens.toArray(new String[0])); + // StringBuilder output = new StringBuilder(); + + // BufferedReader reader = new BufferedReader( + // new InputStreamReader(process.getErrorStream())); + + // BufferedReader reader2 = new BufferedReader( + // new InputStreamReader(process.getInputStream())); + + // String line; + // while ((line = reader.readLine()) != null) { + // output.append(line + "\n"); + // } + + // String line2; + // while ((line2= reader2.readLine()) != null) { + // output.append(line2 + "\n"); + // } + + boolean processResult = process.waitFor(5, TimeUnit.MINUTES); + } catch (IOException | InterruptedException e) { + ; + } + + List metaData; + try { + metaData = readMetaData(outputDirFiles); + } catch (Exception e) { + ; + return null; + } + + ArrayList> attempts = readResponses(outputDirCalls); + + return new NucleiResult(attempts, metaData); + + } + + public static List readMetaData(String nucleiOutputDir) throws FileNotFoundException { + + FileReader fileReader = new FileReader(nucleiOutputDir+"/main.txt"); + BufferedReader reader = new BufferedReader(fileReader); + Gson gson = new Gson(); + + List result = new ArrayList<>(); + String line = ""; + + try { + while ((line = reader.readLine()) != null) { + BasicDBObject value = gson.fromJson(line, BasicDBObject.class); + result.add(value); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + return result; + } + + private final static String dumpReqRegex = "^(\\[.*\\]) Dumped \\w+ request for [^ \\n]+"; + private final static String dumpRespRegex = "^(\\[.*\\]) Dumped \\w+ response [^ \\n]+"; + private enum State { + REQ_HEADER, REQ_PAYLOAD, RESP_HEADER, RESP_PAYLOAD + } + + public static ArrayList> readResponses(String nucleiOutputDir) { + ArrayList> ret = new ArrayList<>(); + + File nucleiOutputDirFile = new File(nucleiOutputDir); + for(File childFile: nucleiOutputDirFile.listFiles()) { + if (childFile.isDirectory()) { + for(File logFile: childFile.listFiles()) { + BufferedReader reader = null; + FileReader fileReader = null; + State state = null; + Pair pair = new Pair<>(new OriginalHttpRequest(), new OriginalHttpResponse()); + try { + fileReader = new FileReader(logFile); + reader = new BufferedReader(fileReader); + String line = ""; + + while (line != null) { + line = reader.readLine(); + if (line == null) break; + State oldState = state; + if (line.matches(dumpReqRegex)) { + pair = new Pair<>(new OriginalHttpRequest(), new OriginalHttpResponse()); + ret.add(pair); + line = reader.readLine(); + line = reader.readLine(); + line = reader.readLine(); + + boolean valid = pair.getFirst().setMethodAndQP(line); + if (!valid) continue; + state = State.REQ_HEADER; + } else if (line.matches(dumpRespRegex)) { + line = reader.readLine(); + line = reader.readLine(); + boolean valid = pair.getSecond().setStatusFromLine(line); + if (!valid) { + continue; + } + state = State.RESP_HEADER; + } else if (line.isEmpty()) { + if(state == State.REQ_HEADER) { + state = State.REQ_PAYLOAD; + } else if(state == State.RESP_HEADER) { + state = State.RESP_PAYLOAD; + } + } + + boolean stateChanged = (oldState != state); + + if (!stateChanged) { + switch (state) { + case REQ_HEADER: + pair.getFirst().addHeaderFromLine(line); + break; + case REQ_PAYLOAD: + pair.getFirst().appendToPayload(line); + break; + case RESP_HEADER: + pair.getSecond().addHeaderFromLine(line); + break; + case RESP_PAYLOAD: + pair.getSecond().appendToPayload(line); + break; + default: + break; + } + } + + } + + } catch (IOException e) { + ; + } finally { + + try { + if (fileReader != null) fileReader.close(); + if (reader != null) reader.close(); + } catch (IOException e) { + + } + } + + } + } + } + + return ret; + } +} \ No newline at end of file diff --git a/apps/testing/src/main/java/com/akto/testing/StatusCodeAnalyser.java b/apps/testing/src/main/java/com/akto/testing/StatusCodeAnalyser.java new file mode 100644 index 0000000000..1045c87084 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/testing/StatusCodeAnalyser.java @@ -0,0 +1,217 @@ +package com.akto.testing; + +import com.akto.dao.AuthMechanismsDao; +import com.akto.dto.*; +import com.akto.dto.testing.AuthMechanism; +import com.akto.store.SampleMessageStore; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.BasicDBObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +import static com.akto.runtime.RelationshipSync.extractAllValuesFromPayload; + +public class StatusCodeAnalyser { + + static ObjectMapper mapper = new ObjectMapper(); + static JsonFactory factory = mapper.getFactory(); + + static List result = new ArrayList<>(); + + public static class StatusCodeIdentifier { + public Set keySet; + public String statusCodeKey; + + public StatusCodeIdentifier(Set keySet, String statusCodeKey) { + this.keySet = keySet; + this.statusCodeKey = statusCodeKey; + } + + @Override + public String toString() { + return keySet + " " + statusCodeKey; + } + } + + private static final Logger logger = LoggerFactory.getLogger(StatusCodeAnalyser.class); + + public static int MAX_COUNT = 30; + public static void run() { + logger.info("Running status analyser"); + Map> sampleDataMap = SampleMessageStore.fetchSampleMessages(); + + AuthMechanism authMechanism = AuthMechanismsDao.instance.findOne(new BasicDBObject()); + if (authMechanism == null) { + logger.error("No auth mechanism"); + return; + } + Map, Map> frequencyMap = new HashMap<>(); + + int count = 0; + int inc = 0; + for (ApiInfo.ApiInfoKey apiInfoKey: sampleDataMap.keySet()) { + if (count > MAX_COUNT) break; + try { + List messages = SampleMessageStore.fetchAllOriginalMessages(apiInfoKey, sampleDataMap); + boolean success = fillFrequencyMap(messages, authMechanism, frequencyMap); + if (success) { + count += 1; + logger.info("count: " + count); + } + } catch (Exception e) { + ; + } + inc += 1; + } + + calculateResult(frequencyMap, 5); + } + + public static void calculateResult(Map, Map> frequencyMap, int threshold) { + if (frequencyMap == null) return; + for (Set params: frequencyMap.keySet()) { + Map countObj = frequencyMap.getOrDefault(params, new HashMap<>()); + for (String key: countObj.keySet()) { + if (countObj.get(key) > threshold) { + result.add(new StatusCodeIdentifier(params, key)); + break; + } + } + } + } + + public static boolean fillFrequencyMap(List messages, AuthMechanism authMechanism, Map, Map> frequencyMap) { + + // fetch sample message + List filteredMessages = SampleMessageStore.filterMessagesWithAuthToken(messages, authMechanism); + if (filteredMessages.isEmpty()) return false; + + RawApi rawApi = filteredMessages.get(0); + + OriginalHttpRequest request = rawApi.getRequest(); + OriginalHttpResponse response = rawApi.getResponse(); + + // discard if payload is null or empty + String originalPayload = response.getBody(); + if (originalPayload == null || originalPayload.equals("{}") || originalPayload.isEmpty()) { + return false; + } + + // if auth token is not passed originally -> skip + boolean result = authMechanism.removeAuthFromRequest(request); + if (!result) return false; + + // execute API + OriginalHttpResponse finalResponse; + try { + finalResponse = ApiExecutor.sendRequest(request, true); + } catch (Exception e) { + return false; + } + + // if non 2xx then skip this api + if (finalResponse.getStatusCode() < 200 || finalResponse.getStatusCode() >= 300) return false; + + // store response keys and values in map + String payload = finalResponse.getBody(); + Map> responseParamMap = new HashMap<>(); + Map> originalResponseParamMap = new HashMap<>(); + try { + JsonParser jp = factory.createParser(payload); + JsonNode node = mapper.readTree(jp); + extractAllValuesFromPayload(node,new ArrayList<>(), responseParamMap); + + jp = factory.createParser(originalPayload); + node = mapper.readTree(jp); + extractAllValuesFromPayload(node,new ArrayList<>(), originalResponseParamMap); + } catch (Exception e) { + return false; + } + + if (responseParamMap.size() > 10) return false; + + List potentialStatusCodeKeys = getPotentialStatusCodeKeys(responseParamMap); + if (potentialStatusCodeKeys.isEmpty()) return false; + + Set params = responseParamMap.keySet(); + Map newCountObj = frequencyMap.get(params); + if (newCountObj != null) { + for (String statusCodeKey: potentialStatusCodeKeys) { + Integer val = newCountObj.getOrDefault(statusCodeKey, 0); + newCountObj.put(statusCodeKey, val + 1); + } + } else { + // Add only if original and current payloads are different + if (originalResponseParamMap.keySet().equals(responseParamMap.keySet())) { + return false; + } + frequencyMap.put(params, new HashMap<>()); + for (String statusCodeKey: potentialStatusCodeKeys) { + frequencyMap.get(params).put(statusCodeKey, 1); + } + } + + + return true; + } + + public static List getPotentialStatusCodeKeys(Map> responseParamMap) { + List potentialStatusCodeKeys = new ArrayList<>(); + for (String key: responseParamMap.keySet()) { + Set val = responseParamMap.get(key); + if (val== null || val.size() != 1) continue; + List valList = new ArrayList<>(val); + String statusCodeString = valList.get(0); + try { + int statusCode = Integer.parseInt(statusCodeString); + if (statusCode < 0 || statusCode > 999 ) continue; + } catch (Exception e) { + continue; + } + potentialStatusCodeKeys.add(key); + break; + } + + return potentialStatusCodeKeys; + } + + public static int getStatusCode(String payload, int statusCode) { + if (statusCode < 200 || statusCode >= 300) return statusCode; + + Map> responseParamMap = new HashMap<>(); + try { + JsonParser jp = factory.createParser(payload); + JsonNode node = mapper.readTree(jp); + extractAllValuesFromPayload(node,new ArrayList<>(), responseParamMap); + } catch (Exception e) { + return statusCode; + } + + for (StatusCodeIdentifier statusCodeIdentifier: result) { + boolean flag = false; + for (String key: statusCodeIdentifier.keySet) { + if (!responseParamMap.containsKey(key)) { + flag = true; + break; + } + } + + if (flag) continue; + + Set val = responseParamMap.get(statusCodeIdentifier.statusCodeKey); + if (val == null || val.isEmpty()) continue; + String vv = val.iterator().next(); + try { + return Integer.parseInt(vv); + } catch (Exception ignored) { } + } + + return statusCode; + } + +} diff --git a/apps/testing/src/main/java/com/akto/testing/TestExecutor.java b/apps/testing/src/main/java/com/akto/testing/TestExecutor.java new file mode 100644 index 0000000000..b3d4cf0c31 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/testing/TestExecutor.java @@ -0,0 +1,496 @@ +package com.akto.testing; + +import com.akto.DaoInit; +import com.akto.dao.AuthMechanismsDao; +import com.akto.dao.context.Context; +import com.akto.dao.testing.TestingRunDao; +import com.akto.dao.testing.TestingRunResultDao; +import com.akto.dao.testing.TestingRunResultSummariesDao; +import com.akto.dao.testing.WorkflowTestsDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.testing.*; +import com.akto.dto.testing.TestingRun.State; +import com.akto.dto.testing.sources.TestSourceConfig; +import com.akto.dto.type.RequestTemplate; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods; +import com.akto.log.LoggerMaker; +import com.akto.rules.*; +import com.akto.store.SampleMessageStore; +import com.akto.store.TestingUtil; +import com.akto.testing_issues.TestingIssuesHandler; +import com.akto.util.JSONUtils; +import com.akto.util.enums.LoginFlowEnums; +import com.akto.util.enums.GlobalEnums.TestSubCategory; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import org.bson.types.ObjectId; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.concurrent.*; + +public class TestExecutor { + + private static final LoggerMaker loggerMaker = new LoggerMaker(TestExecutor.class); + private static final Logger logger = LoggerFactory.getLogger(TestExecutor.class); + + + + public void init(TestingRun testingRun, ObjectId summaryId) { + if (testingRun.getTestIdConfig() != 1) { + apiWiseInit(testingRun, summaryId); + } else { + workflowInit(testingRun, summaryId); + } + } + + public static void main(String[] args) { + DaoInit.init(new ConnectionString("mongodb://localhost:27017/admini")); + Context.accountId.set(1_000_000); + + TestExecutor testExecutor = new TestExecutor(); + TestingRun testingRun = TestingRunDao.instance.findOne(new BasicDBObject()); + testExecutor.init(testingRun, new ObjectId()); + } + + public void workflowInit (TestingRun testingRun, ObjectId summaryId) { + TestingEndpoints testingEndpoints = testingRun.getTestingEndpoints(); + if (!testingEndpoints.getType().equals(TestingEndpoints.Type.WORKFLOW)) { + loggerMaker.errorAndAddToDb("Invalid workflow type"); + return; + } + + WorkflowTestingEndpoints workflowTestingEndpoints = (WorkflowTestingEndpoints) testingEndpoints; + WorkflowTest workflowTestOld = workflowTestingEndpoints.getWorkflowTest(); + + WorkflowTest workflowTest = WorkflowTestsDao.instance.findOne( + Filters.eq("_id", workflowTestOld.getId()) + ); + + if (workflowTest == null) { + loggerMaker.errorAndAddToDb("Workflow test has been deleted"); + return ; + } + + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + try { + apiWorkflowExecutor.init(workflowTest, testingRun.getId(), summaryId); + } catch (Exception e) { + ; + } + + Map totalCountIssues = new HashMap<>(); + totalCountIssues.put("HIGH", 0); + totalCountIssues.put("MEDIUM", 0); + totalCountIssues.put("LOW", 0); + + TestingRunResultSummariesDao.instance.updateOne( + Filters.eq("_id", summaryId), + Updates.combine( + Updates.set(TestingRunResultSummary.END_TIMESTAMP, Context.now()), + Updates.set(TestingRunResultSummary.STATE, State.COMPLETED), + Updates.set(TestingRunResultSummary.COUNT_ISSUES, totalCountIssues) + ) + ); + } + + public void apiWiseInit(TestingRun testingRun, ObjectId summaryId) { + int accountId = Context.accountId.get(); + int now = Context.now(); + int maxConcurrentRequests = testingRun.getMaxConcurrentRequests() > 0 ? testingRun.getMaxConcurrentRequests() : 100; + TestingEndpoints testingEndpoints = testingRun.getTestingEndpoints(); + + Map singleTypeInfoMap = SampleMessageStore.buildSingleTypeInfoMap(testingEndpoints); + Map> sampleMessages = SampleMessageStore.fetchSampleMessages(); + List testRoles = SampleMessageStore.fetchTestRoles(); + AuthMechanism authMechanism = AuthMechanismsDao.instance.findOne(new BasicDBObject()); + + TestingUtil testingUtil = new TestingUtil(authMechanism, sampleMessages, singleTypeInfoMap, testRoles); + + try { + LoginFlowResponse loginFlowResponse = triggerLoginFlow(authMechanism, 3); + if (!loginFlowResponse.getSuccess()) { + logger.error("login flow failed"); + throw new Exception("login flow failed"); + } + } catch (Exception e) { + loggerMaker.errorAndAddToDb(e.getMessage()); + return; + } + + List apiInfoKeyList = testingEndpoints.returnApis(); + if (apiInfoKeyList == null || apiInfoKeyList.isEmpty()) return; + + loggerMaker.infoAndAddToDb("APIs found: " + apiInfoKeyList.size()); + + TestingRunResultSummariesDao.instance.updateOne( + Filters.eq("_id", summaryId), + Updates.set(TestingRunResultSummary.TOTAL_APIS, apiInfoKeyList.size())); + + CountDownLatch latch = new CountDownLatch(apiInfoKeyList.size()); + ExecutorService threadPool = Executors.newFixedThreadPool(maxConcurrentRequests); + List>> futureTestingRunResults = new ArrayList<>(); + for (ApiInfo.ApiInfoKey apiInfoKey: apiInfoKeyList) { + try { + Future> future = threadPool.submit( + () -> startWithLatch(apiInfoKey, + testingRun.getTestIdConfig(), + testingRun.getId(),testingRun.getTestingRunConfig(), testingUtil, summaryId, + accountId, latch, now, testingRun.getTestRunTime())); + futureTestingRunResults.add(future); + } catch (Exception e) { + loggerMaker.errorAndAddToDb("Error in API " + apiInfoKey + " : " + e.getMessage()); + } + } + + loggerMaker.infoAndAddToDb("Waiting..."); + + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + loggerMaker.infoAndAddToDb("Finished testing"); + + List testingRunResults = new ArrayList<>(); + for (Future> future: futureTestingRunResults) { + if (!future.isDone()) continue; + try { + if (!future.get().isEmpty()) { + testingRunResults.addAll(future.get()); + } + } catch (InterruptedException | ExecutionException e) { + ; + } + } + + loggerMaker.infoAndAddToDb("Finished adding " + testingRunResults.size() + " testingRunResults"); + + TestingRunResultSummariesDao.instance.updateOne( + Filters.eq("_id", summaryId), + Updates.set(TestingRunResultSummary.TEST_RESULTS_COUNT, testingRunResults.size()) + ); + + //Creating issues from testingRunResults + TestingIssuesHandler handler = new TestingIssuesHandler(); + handler.handleIssuesCreationFromTestingRunResults(testingRunResults); + + loggerMaker.infoAndAddToDb("Finished adding issues"); + + Map totalCountIssues = new HashMap<>(); + totalCountIssues.put("HIGH", 0); + totalCountIssues.put("MEDIUM", 0); + totalCountIssues.put("LOW", 0); + + for (TestingRunResult testingRunResult: testingRunResults) { + if (testingRunResult.isVulnerable()) { + int initialCount = totalCountIssues.get("HIGH"); + totalCountIssues.put("HIGH", initialCount + 1); + } + } + + TestingRunResultSummariesDao.instance.updateOne( + Filters.eq("_id", summaryId), + Updates.combine( + Updates.set(TestingRunResultSummary.END_TIMESTAMP, Context.now()), + Updates.set(TestingRunResultSummary.STATE, State.COMPLETED), + Updates.set(TestingRunResultSummary.COUNT_ISSUES, totalCountIssues) + ) + ); + + loggerMaker.infoAndAddToDb("Finished updating TestingRunResultSummariesDao"); + + } + + private LoginFlowResponse triggerLoginFlow(AuthMechanism authMechanism, int retries) { + LoginFlowResponse loginFlowResponse = null; + for (int i=0; i requestData, LoginFlowParams loginFlowParams) { + + String source, target; + List edges = new ArrayList<>(); + int edgeNumber = 1; + LoginWorkflowGraphEdge edgeObj; + Map mapNodeIdToWorkflowNodeDetails = new HashMap<>(); + for (int i=0; i< requestData.size(); i++) { + + RequestData data = requestData.get(i); + + source = (i==0)? "1" : "x"+ (edgeNumber - 2); + target = "x"+ edgeNumber; + edgeNumber += 2; + + edgeObj = new LoginWorkflowGraphEdge(source, target, target); + edges.add(edgeObj.toString()); + + JSONObject json = new JSONObject() ; + json.put("method", data.getMethod()); + json.put("requestPayload", data.getBody()); + json.put("path", data.getUrl()); + json.put("requestHeaders", data.getHeaders()); + json.put("type", ""); + + WorkflowUpdatedSampleData sampleData = new WorkflowUpdatedSampleData(json.toString(), data.getQueryParams(), + data.getHeaders(), data.getBody(), data.getUrl()); + + int waitTime = 0; + WorkflowNodeDetails.Type nodeType = WorkflowNodeDetails.Type.API; + if (data.getType().equals(LoginFlowEnums.LoginStepTypesEnums.OTP_VERIFICATION.toString())) { + nodeType = WorkflowNodeDetails.Type.OTP; + if (loginFlowParams == null || !loginFlowParams.getFetchValueMap()) { + waitTime = 60; + } + } + if (data.getType().equals(LoginFlowEnums.LoginStepTypesEnums.RECORDED_FLOW.toString())) { + nodeType = WorkflowNodeDetails.Type.RECORDED; + } + WorkflowNodeDetails workflowNodeDetails = new WorkflowNodeDetails(0, data.getUrl(), + URLMethods.Method.fromString(data.getMethod()), "", sampleData, + nodeType, true, waitTime, 0, 0, data.getRegex(), data.getOtpRefUuid()); + + mapNodeIdToWorkflowNodeDetails.put(target, workflowNodeDetails); + } + + edgeObj = new LoginWorkflowGraphEdge("x"+ (edgeNumber - 2), "3", "x"+ edgeNumber); + edges.add(edgeObj.toString()); + + return new WorkflowTest(0, 0, "", Context.now(), "", Context.now(), + null, edges, mapNodeIdToWorkflowNodeDetails, WorkflowTest.State.DRAFT); + } + + public Map generateResponseMap(String payloadStr, Map> headers) { + boolean isList = false; + + Map respMap = new HashMap<>(); + + if (payloadStr == null) payloadStr = "{}"; + if (payloadStr.startsWith("[")) { + payloadStr = "{\"json\": "+payloadStr+"}"; + isList = true; + } + + BasicDBObject payloadObj; + try { + payloadObj = BasicDBObject.parse(payloadStr); + } catch (Exception e) { + boolean isPostFormData = payloadStr.contains("&") && payloadStr.contains("="); + if (isPostFormData) { + String mockUrl = "url?"+ payloadStr; // because getQueryJSON function needs complete url + payloadObj = RequestTemplate.getQueryJSON(mockUrl); + } else { + payloadObj = BasicDBObject.parse("{}"); + } + } + + Object obj; + if (isList) { + obj = payloadObj.get("json"); + } else { + obj = payloadObj; + } + + BasicDBObject flattened = JSONUtils.flattenWithDots(obj); + + + for (String param: flattened.keySet()) { + respMap.put(param, flattened.get(param)); + } + + for (String headerName: headers.keySet()) { + for (String val: headers.get(headerName)) { + respMap.put(headerName, val); + } + } + return respMap; + } + + public List startWithLatch( + ApiInfo.ApiInfoKey apiInfoKey, int testIdConfig, ObjectId testRunId, TestingRunConfig testingRunConfig, + TestingUtil testingUtil, ObjectId testRunResultSummaryId, int accountId, CountDownLatch latch, int startTime, int timeToKill) { + + Context.accountId.set(accountId); + List testingRunResults = new ArrayList<>(); + int now = Context.now(); + if ( timeToKill <= 0 || now - startTime <= timeToKill) { + try { + testingRunResults = start(apiInfoKey, testIdConfig, testRunId, testingRunConfig, testingUtil, testRunResultSummaryId); + TestingRunResultDao.instance.insertMany(testingRunResults); + } catch (Exception e) { + e.printStackTrace(); + } + } + + latch.countDown(); + return testingRunResults; + } + + public List start(ApiInfo.ApiInfoKey apiInfoKey, int testIdConfig, ObjectId testRunId, + TestingRunConfig testingRunConfig, TestingUtil testingUtil, ObjectId testRunResultSummaryId) { + + if (testIdConfig == 1) { + loggerMaker.errorAndAddToDb("Test id config is 1"); + return new ArrayList<>(); + } + + List testSubCategories = testingRunConfig == null ? null : testingRunConfig.getTestSubCategoryList(); + + BOLATest bolaTest = new BOLATest();//REPLACE_AUTH_TOKEN + NoAuthTest noAuthTest = new NoAuthTest();//REMOVE_TOKENS + ChangeHttpMethodTest changeHttpMethodTest = new ChangeHttpMethodTest();//CHANGE_METHOD + AddMethodInParameterTest addMethodInParameterTest = new AddMethodInParameterTest();//ADD_METHOD_IN_PARAMETER + AddMethodOverrideHeadersTest addMethodOverrideHeadersTest = new AddMethodOverrideHeadersTest();//ADD_METHOD_OVERRIDE_HEADERS + AddUserIdTest addUserIdTest = new AddUserIdTest();//ADD_USER_ID + ParameterPollutionTest parameterPollutionTest = new ParameterPollutionTest();//PARAMETER_POLLUTION + OldApiVersionTest oldApiVersionTest = new OldApiVersionTest();//REPLACE_AUTH_TOKEN_OLD_VERSION + JWTNoneAlgoTest jwtNoneAlgoTest = new JWTNoneAlgoTest();//JWT_NONE_ALGO + JWTInvalidSignatureTest jwtInvalidSignatureTest = new JWTInvalidSignatureTest();//JWT_INVALID_SIGNATURE + AddJkuToJwtTest addJkuToJwtTest = new AddJkuToJwtTest();//ADD_JKU_TO_JWT + BFLATest bflaTest = new BFLATest();//BFLA + + List testingRunResults = new ArrayList<>(); + + TestingRunResult noAuthTestResult = runTest(noAuthTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (noAuthTestResult != null) testingRunResults.add(noAuthTestResult); + if (noAuthTestResult != null && !noAuthTestResult.isVulnerable()) { + + TestPlugin.TestRoleMatcher testRoleMatcher = new TestPlugin.TestRoleMatcher(testingUtil.getTestRoles(), apiInfoKey); + if ((testSubCategories == null || testSubCategories.contains(TestSubCategory.BFLA.name())) && testRoleMatcher.shouldDoBFLA()) { + TestingRunResult bflaTestResult = runTest(bflaTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (bflaTestResult != null) testingRunResults.add(bflaTestResult); + } else if (testSubCategories == null || testSubCategories.contains(TestSubCategory.REPLACE_AUTH_TOKEN.name())){ + TestingRunResult bolaTestResult = runTest(bolaTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (bolaTestResult != null) testingRunResults.add(bolaTestResult); + } + + if (testSubCategories == null || testSubCategories.contains(TestSubCategory.ADD_USER_ID.name())) { + TestingRunResult addUserIdTestResult = runTest(addUserIdTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (addUserIdTestResult != null) testingRunResults.add(addUserIdTestResult); + } + + if (testSubCategories == null || testSubCategories.contains(TestSubCategory.PARAMETER_POLLUTION.name())) { + TestingRunResult parameterPollutionTestResult = runTest(parameterPollutionTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (parameterPollutionTestResult != null) testingRunResults.add(parameterPollutionTestResult); + } + + if (testSubCategories == null || testSubCategories.contains(TestSubCategory.REPLACE_AUTH_TOKEN_OLD_VERSION.name())) { + TestingRunResult oldApiVersionTestResult = runTest(oldApiVersionTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (oldApiVersionTestResult != null) testingRunResults.add(oldApiVersionTestResult); + } + + if (testSubCategories == null || testSubCategories.contains(TestSubCategory.JWT_NONE_ALGO.name())) { + TestingRunResult jwtNoneAlgoTestResult = runTest(jwtNoneAlgoTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (jwtNoneAlgoTestResult != null) testingRunResults.add(jwtNoneAlgoTestResult); + } + + if (testSubCategories == null || testSubCategories.contains(TestSubCategory.JWT_INVALID_SIGNATURE.name())) { + TestingRunResult jwtInvalidSignatureTestResult = runTest(jwtInvalidSignatureTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (jwtInvalidSignatureTestResult != null) testingRunResults.add(jwtInvalidSignatureTestResult); + } + + if (testSubCategories == null || testSubCategories.contains(TestSubCategory.ADD_JKU_TO_JWT.name())) { + TestingRunResult addJkuToJwtTestResult = runTest(addJkuToJwtTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (addJkuToJwtTestResult != null) testingRunResults.add(addJkuToJwtTestResult); + } + } + + if (testSubCategories == null || testSubCategories.contains(TestSubCategory.ADD_METHOD_IN_PARAMETER.name())) { + TestingRunResult addMethodInParameterTestResult = runTest(addMethodInParameterTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (addMethodInParameterTestResult != null) testingRunResults.add(addMethodInParameterTestResult); + } + + if (testSubCategories == null || testSubCategories.contains(TestSubCategory.ADD_METHOD_OVERRIDE_HEADERS.name())) { + TestingRunResult addMethodOverrideHeadersTestResult = runTest(addMethodOverrideHeadersTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (addMethodOverrideHeadersTestResult != null) testingRunResults.add(addMethodOverrideHeadersTestResult); + } + + if (testSubCategories == null || testSubCategories.contains(TestSubCategory.CHANGE_METHOD.name())) { + TestingRunResult changeHttpMethodTestResult = runTest(changeHttpMethodTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (changeHttpMethodTestResult != null) testingRunResults.add(changeHttpMethodTestResult); + } + + if (testSubCategories != null) { + for (String testSubCategory: testSubCategories) { + if (testSubCategory.startsWith("http://") || testSubCategory.startsWith("https://")) { + try { + String origTemplateURL = testSubCategory; + + origTemplateURL = origTemplateURL.replace("https://github.com/", "https://raw.githubusercontent.com/").replace("/blob/", "/"); + + String subcategory = origTemplateURL.substring(origTemplateURL.lastIndexOf("/")+1).split("\\.")[0]; + FuzzingTest fuzzingTest = new FuzzingTest(testRunId.toHexString(), testRunResultSummaryId.toHexString(), origTemplateURL, subcategory, testSubCategory); + TestingRunResult fuzzResult = runTest(fuzzingTest, apiInfoKey, testingUtil, testRunId, testRunResultSummaryId); + if (fuzzResult != null) testingRunResults.add(fuzzResult); + } catch (Exception e) { + loggerMaker.errorAndAddToDb("unable to execute fuzzing for " + testSubCategory); + ; + } + } + } + } + + return testingRunResults; + } + + public TestingRunResult runTest(TestPlugin testPlugin, ApiInfo.ApiInfoKey apiInfoKey, TestingUtil testingUtil, ObjectId testRunId, ObjectId testRunResultSummaryId) { + + int startTime = Context.now(); + TestPlugin.Result result = testPlugin.start(apiInfoKey, testingUtil); + if (result == null) return null; + int endTime = Context.now(); + + String subTestName = testPlugin.subTestName(); + + if (testPlugin instanceof FuzzingTest) { + FuzzingTest test = (FuzzingTest) testPlugin; + subTestName = test.getTestSourceConfigCategory(); + } + + return new TestingRunResult( + testRunId, apiInfoKey, testPlugin.superTestName(), subTestName, result.testResults, + result.isVulnerable,result.singleTypeInfos, result.confidencePercentage, + startTime, endTime, testRunResultSummaryId + ); + } + +} diff --git a/apps/testing/src/main/java/com/akto/testing_issues/TestingIssuesHandler.java b/apps/testing/src/main/java/com/akto/testing_issues/TestingIssuesHandler.java new file mode 100644 index 0000000000..ea02e70de2 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/testing_issues/TestingIssuesHandler.java @@ -0,0 +1,149 @@ +package com.akto.testing_issues; + +import com.akto.dao.context.Context; +import com.akto.dao.testing.sources.TestSourceConfigsDao; +import com.akto.dao.testing_run_findings.TestingRunIssuesDao; +import com.akto.dto.test_run_findings.TestingIssuesId; +import com.akto.dto.test_run_findings.TestingRunIssues; +import com.akto.dto.testing.TestingRunResult; +import com.akto.dto.testing.sources.TestSourceConfig; +import com.akto.testing_utils.TestingUtils; +import com.mongodb.bulk.BulkWriteResult; +import com.mongodb.client.model.*; +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.akto.util.Constants.ID; +import static com.akto.util.enums.GlobalEnums.*; + +public class TestingIssuesHandler { + + private static final Logger logger = LoggerFactory.getLogger(TestingIssuesHandler.class); + //Update one Write models + /* + * Checks the status of issue from db, + * + * */ + + private void writeUpdateQueryIntoWriteModel(List> writeModelList, + Map testingIssuesIdsMap, + List testingRunIssuesList) { + int lastSeen = Context.now(); + + testingRunIssuesList.forEach(testingRunIssues -> { + TestingIssuesId issuesId = testingRunIssues.getId(); + + TestingRunResult runResult = testingIssuesIdsMap.get(getIssuesIdFromMap(issuesId, testingIssuesIdsMap)); + if (runResult == null) { + return; + } + TestRunIssueStatus status = testingRunIssues.getTestRunIssueStatus(); + Bson query = Filters.eq(ID, issuesId); + Bson updateStatusFields; + Bson updateSeverityField; + if (runResult.isVulnerable()) { + if (status == TestRunIssueStatus.IGNORED) { + updateStatusFields = Updates.set(TestingRunIssues.TEST_RUN_ISSUES_STATUS, TestRunIssueStatus.IGNORED); + } else { + updateStatusFields = Updates.set(TestingRunIssues.TEST_RUN_ISSUES_STATUS, TestRunIssueStatus.OPEN); + } + } else { + updateStatusFields = Updates.set(TestingRunIssues.TEST_RUN_ISSUES_STATUS, TestRunIssueStatus.FIXED); + } + + TestSubCategory subCategory = TestSubCategory.getTestCategory(runResult.getTestSubType()); + if (subCategory == null) {//TestSourceConfig case + TestSourceConfig config = TestSourceConfigsDao.instance.getTestSourceConfig(runResult.getTestSubType()); + updateSeverityField = Updates.set(TestingRunIssues.KEY_SEVERITY, config.getSeverity()); + } else {//TestSubCategory case + updateSeverityField = Updates.set(TestingRunIssues.KEY_SEVERITY, + subCategory.getSuperCategory().getSeverity()); + } + + Bson updateFields = Updates.combine( + updateStatusFields, + updateSeverityField, + Updates.set(TestingRunIssues.LAST_SEEN, lastSeen), + Updates.set(TestingRunIssues.LATEST_TESTING_RUN_SUMMARY_ID, runResult.getTestRunResultSummaryId()) + ); + logger.info("Updating the issue with id {}, with update parameters and result_summary_Id :{} ", issuesId + ,runResult.getTestRunResultSummaryId()); + + writeModelList.add(new UpdateOneModel<>(query, updateFields)); + }); + + } + + private TestingIssuesId getIssuesIdFromMap(TestingIssuesId issuesId, + Map testingIssuesIdsMap) { + for (TestingIssuesId testingIssuesId : testingIssuesIdsMap.keySet()) { + if (testingIssuesId.equals(issuesId)) { + return testingIssuesId; + } + } + return new TestingIssuesId(); + } + + private void insertVulnerableTestsIntoIssuesCollection(List> writeModelList, + Map testingIssuesIdsMap, + List testingRunIssuesList) { + int lastSeen = Context.now(); + testingIssuesIdsMap.forEach((testingIssuesId, runResult) -> { + boolean doesExists = false; + for (TestingRunIssues testingRunIssues : testingRunIssuesList) { + if (testingRunIssues.getId().equals(testingIssuesId)) { + doesExists = true; + break; + } + } + if (!doesExists && runResult.isVulnerable()) { + TestSubCategory subCategory = TestSubCategory.getTestCategory(runResult.getTestSubType()); + if (subCategory == null) { + TestSourceConfig config = TestSourceConfigsDao.instance.getTestSourceConfig(runResult.getTestSubType()); + writeModelList.add(new InsertOneModel<>(new TestingRunIssues(testingIssuesId, + config.getSeverity(), + TestRunIssueStatus.OPEN, lastSeen, lastSeen, runResult.getTestRunResultSummaryId()))); + }else { + writeModelList.add(new InsertOneModel<>(new TestingRunIssues(testingIssuesId, + subCategory.getSuperCategory().getSeverity(), + TestRunIssueStatus.OPEN, lastSeen, lastSeen, runResult.getTestRunResultSummaryId()))); + } + logger.info("Inserting the id {} , with summary Id as {}", testingIssuesId, runResult.getTestRunResultSummaryId()); + } + }); + } + + public void handleIssuesCreationFromTestingRunResults(List testingRunResultList) { + + Map testingIssuesIdsMap = TestingUtils. + listOfIssuesIdsFromTestingRunResults(testingRunResultList, true); + + logger.info("Total issue id created from TestingRunResult map : {}", testingIssuesIdsMap.size()); + Bson inQuery = Filters.in(ID, testingIssuesIdsMap.keySet().toArray()); + List testingRunIssuesList = TestingRunIssuesDao.instance.findAll(inQuery); + + logger.info("Total list of issues from db : {}", testingRunIssuesList.size()); + List> writeModelList = new ArrayList<>(); + writeUpdateQueryIntoWriteModel(writeModelList, testingIssuesIdsMap, testingRunIssuesList); + logger.info("Total write queries after the update iterations: {}", writeModelList.size()); + insertVulnerableTestsIntoIssuesCollection(writeModelList, testingIssuesIdsMap, testingRunIssuesList); + logger.info("Total write queries after the insertion iterations: {}", writeModelList.size()); + try { + if (writeModelList.size() > 0) { + BulkWriteResult result = TestingRunIssuesDao.instance.bulkWrite(writeModelList, new BulkWriteOptions().ordered(false)); + logger.info("Matched records : {}", result.getMatchedCount()); + logger.info("inserted counts : {}", result.getInsertedCount()); + logger.info("Modified counts : {}", result.getModifiedCount()); + } else { + logger.info("writeModelList is empty"); + } + } catch (Exception e) { + logger.error("Error while inserting issues into db: {}", e.getMessage()); + } + } +} diff --git a/apps/testing/src/main/resources/nuclei_linux b/apps/testing/src/main/resources/nuclei_linux new file mode 100755 index 0000000000..b5fdce4ce5 Binary files /dev/null and b/apps/testing/src/main/resources/nuclei_linux differ diff --git a/apps/testing/src/main/resources/nuclei_m1 b/apps/testing/src/main/resources/nuclei_m1 new file mode 100755 index 0000000000..2b76c61354 Binary files /dev/null and b/apps/testing/src/main/resources/nuclei_m1 differ diff --git a/apps/testing/src/test/java/com/akto/rules/TestFuzzingTest.java b/apps/testing/src/test/java/com/akto/rules/TestFuzzingTest.java new file mode 100644 index 0000000000..7f3bc376f3 --- /dev/null +++ b/apps/testing/src/test/java/com/akto/rules/TestFuzzingTest.java @@ -0,0 +1,325 @@ +package com.akto.rules; + +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.OriginalHttpResponse; +import com.akto.testing.NucleiExecutor; +import com.akto.util.Pair; +import com.mongodb.BasicDBObject; +import kotlin.text.Charsets; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +public class TestFuzzingTest { + + @Rule + public TemporaryFolder templatesFolder = new TemporaryFolder(); + + @Rule + public TemporaryFolder utilsFolder = new TemporaryFolder(); + + @Rule + public TemporaryFolder nucleiOutputFolder = new TemporaryFolder(); + + private File[] testDownloadLinkMain(String value, String templateName) throws Exception { + File file = templatesFolder.newFile(templateName); + + FileUtils.writeStringToFile(file, value, Charsets.UTF_8); + String outputDirectory = utilsFolder.getRoot().getPath(); + + FuzzingTest.downloadLinks(file.getPath(),outputDirectory); + + return new File(outputDirectory).listFiles(); + } + + @Test + public void testDownloadLinksSwagger() throws Exception { + // contains a rawGithubLink in payloads so should generate payloadFile + File[] files = testDownloadLinkMain(generateSwaggerFileDetectionText(), "swagger_file_detection.yaml"); + assertEquals(1, files.length); + assertEquals("swagger_pathlist.txt", files[0].getName()); + } + + @Test + public void testDownloadLinksWordpress() throws Exception { + // contains a local file link in payloads so should NOT generate payloadFile + File[] files = testDownloadLinkMain(generateWordpressPluginsDetect(), "wordpress-plugins-detect.yaml"); + assertEquals(0, files.length); + } + + @Test + public void testDownloadLinksGmail() throws Exception { + // contains a local file link in payloads so should NOT generate payloadFile + File[] files = testDownloadLinkMain(generateValidGmailCheck(), "valid-gmail-check.yaml"); + assertEquals(0, files.length); + } + + @Test + public void testReadMetaData() throws IOException { + File file = nucleiOutputFolder.newFile("main.txt"); + FileUtils.writeStringToFile(file, "", Charsets.UTF_8); + + List metaData = NucleiExecutor.readMetaData(nucleiOutputFolder.getRoot().getPath()); + assertEquals(0, metaData.size()); + + boolean deleteSuccessful = file.delete(); + assertTrue(deleteSuccessful); + + file = nucleiOutputFolder.newFile("main.txt"); + FileUtils.writeStringToFile(file, generateMetaFile(), Charsets.UTF_8); + + metaData = NucleiExecutor.readMetaData(nucleiOutputFolder.getRoot().getPath()); + assertEquals(4, metaData.size()); + } + + + @Test + public void testReadResponses() throws IOException { + String path = nucleiOutputFolder.getRoot()+"/calls/http/random.txt"; + File file = new File(path); + FileUtils.writeStringToFile(file, generateRequestResponseText(), Charsets.UTF_8); + + ArrayList> responses = NucleiExecutor.readResponses(nucleiOutputFolder.getRoot().getPath()+"/calls"); + assertEquals(4, responses.size()); + } + + private String generateRequestResponseText() { + return "[swagger-version] Dumped HTTP request for https://petstore.swagger.io/v2/pet/findByStatus/swagger-ui/swagger-ui.js\n" + + "\n" + + "\n" + + "GET /v2/pet/findByStatus/swagger-ui/swagger-ui.js HTTP/1.1\n" + + "Host: petstore.swagger.io\n" + + "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\n" + + "Connection: close\n" + + "Accept: application/json\n" + + "Accept-Encoding: gzip, deflate, br\n" + + "Accept-Language: en-US,en;q=0.5\n" + + "Connection: keep-alive\n" + + "Referer: https://petstore.swagger.io/\n" + + "Sec-Fetch-Dest: empty\n" + + "Sec-Fetch-Mode: cors\n" + + "Sec-Fetch-Site: same-origin\n" + + "Te: trailers\n" + + "\n" + + "\n" + + "[swagger-version] Dumped HTTP response https://petstore.swagger.io/v2/pet/findByStatus/swagger-ui/swagger-ui.js\n" + + "\n" + + "HTTP/1.1 404 Not Found\n" + + "Transfer-Encoding: chunked\n" + + "Access-Control-Allow-Headers: Content-Type, api_key, Authorization\n" + + "Access-Control-Allow-Methods: GET, POST, DELETE, PUT\n" + + "Access-Control-Allow-Origin: *\n" + + "Connection: keep-alive\n" + + "Content-Type: application/json\n" + + "Date: Thu, 05 Jan 2023 04:20:13 GMT\n" + + "Server: Jetty(9.2.9.v20150224)\n" + + "\n" + + "{\"code\":404,\"type\":\"unknown\",\"message\":\"null for uri: http://petstore.swagger.io/v2/pet/findByStatus/swagger-ui/swagger-ui.js\"}\n" + + "[swagger-version] Dumped HTTP request for https://petstore.swagger.io/v2/pet/findByStatus/swagger/swagger-ui.js\n" + + "\n" + + "\n" + + "GET /v2/pet/findByStatus/swagger/swagger-ui.js HTTP/1.1\n" + + "Host: petstore.swagger.io\n" + + "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\n" + + "Connection: close\n" + + "Accept: application/json\n" + + "Accept-Encoding: gzip, deflate, br\n" + + "Accept-Language: en-US,en;q=0.5\n" + + "Connection: keep-alive\n" + + "Referer: https://petstore.swagger.io/\n" + + "Sec-Fetch-Dest: empty\n" + + "Sec-Fetch-Mode: cors\n" + + "Sec-Fetch-Site: same-origin\n" + + "Te: trailers\n" + + "\n" + + "\n" + + "[swagger-version] Dumped HTTP response https://petstore.swagger.io/v2/pet/findByStatus/swagger/swagger-ui.js\n" + + "\n" + + "HTTP/1.1 404 Not Found\n" + + "Transfer-Encoding: chunked\n" + + "Access-Control-Allow-Headers: Content-Type, api_key, Authorization\n" + + "Access-Control-Allow-Methods: GET, POST, DELETE, PUT\n" + + "Access-Control-Allow-Origin: *\n" + + "Connection: keep-alive\n" + + "Content-Type: application/json\n" + + "Date: Thu, 05 Jan 2023 04:20:13 GMT\n" + + "Server: Jetty(9.2.9.v20150224)\n" + + "\n" + + "{\"code\":404,\"type\":\"unknown\",\"message\":\"null for uri: http://petstore.swagger.io/v2/pet/findByStatus/swagger/swagger-ui.js\"}\n" + + "[swagger-version] Dumped HTTP request for https://petstore.swagger.io/v2/pet/findByStatus/swagger-ui.js\n" + + "\n" + + "\n" + + "GET /v2/pet/findByStatus/swagger-ui.js HTTP/1.1\n" + + "Host: petstore.swagger.io\n" + + "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\n" + + "Connection: close\n" + + "Accept: application/json\n" + + "Accept-Encoding: gzip, deflate, br\n" + + "Accept-Language: en-US,en;q=0.5\n" + + "Connection: keep-alive\n" + + "Referer: https://petstore.swagger.io/\n" + + "Sec-Fetch-Dest: empty\n" + + "Sec-Fetch-Mode: cors\n" + + "Sec-Fetch-Site: same-origin\n" + + "Te: trailers\n" + + "\n" + + "\n" + + "[swagger-version] Dumped HTTP response https://petstore.swagger.io/v2/pet/findByStatus/swagger-ui.js\n" + + "\n" + + "HTTP/1.1 404 Not Found\n" + + "Transfer-Encoding: chunked\n" + + "Access-Control-Allow-Headers: Content-Type, api_key, Authorization\n" + + "Access-Control-Allow-Methods: GET, POST, DELETE, PUT\n" + + "Access-Control-Allow-Origin: *\n" + + "Connection: keep-alive\n" + + "Content-Type: application/json\n" + + "Date: Thu, 05 Jan 2023 04:20:13 GMT\n" + + "Server: Jetty(9.2.9.v20150224)\n" + + "\n" + + "{\"code\":404,\"type\":\"unknown\",\"message\":\"null for uri: http://petstore.swagger.io/v2/pet/findByStatus/swagger-ui.js\"}\n" + + "[swagger-version] Dumped HTTP request for https://petstore.swagger.io/v2/pet/findByStatus/swagger/ui/swagger-ui.js\n" + + "\n" + + "\n" + + "GET /v2/pet/findByStatus/swagger/ui/swagger-ui.js HTTP/1.1\n" + + "Host: petstore.swagger.io\n" + + "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\n" + + "Connection: close\n" + + "Accept: application/json\n" + + "Accept-Encoding: gzip, deflate, br\n" + + "Accept-Language: en-US,en;q=0.5\n" + + "Connection: keep-alive\n" + + "Referer: https://petstore.swagger.io/\n" + + "Sec-Fetch-Dest: empty\n" + + "Sec-Fetch-Mode: cors\n" + + "Sec-Fetch-Site: same-origin\n" + + "Te: trailers\n" + + "\n" + + "\n" + + "[swagger-version] Dumped HTTP response https://petstore.swagger.io/v2/pet/findByStatus/swagger/ui/swagger-ui.js\n" + + "\n" + + "HTTP/1.1 404 Not Found\n" + + "Transfer-Encoding: chunked\n" + + "Access-Control-Allow-Headers: Content-Type, api_key, Authorization\n" + + "Access-Control-Allow-Methods: GET, POST, DELETE, PUT\n" + + "Access-Control-Allow-Origin: *\n" + + "Connection: keep-alive\n" + + "Content-Type: application/json\n" + + "Date: Thu, 05 Jan 2023 04:20:13 GMT\n" + + "Server: Jetty(9.2.9.v20150224)\n" + + "\n" + + "{\"code\":404,\"type\":\"unknown\",\"message\":\"null for uri: http://petstore.swagger.io/v2/pet/findByStatus/swagger/ui/swagger-ui.js\"}"; + } + + private String generateMetaFile() { + return "{\"template-id\":\"swagger-version\",\"info\":{\"name\":\"Swagger 2.x Version Detection\",\"author\":[\"c-sh0\"],\"tags\":[\"tech\",\"swagger\",\"api\"],\"description\":\"Obtain Swagger Version\",\"reference\":[\"swagger ui 2.x and under\",\"https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/version-detection.md\"],\"severity\":\"info\"},\"type\":\"http\",\"host\":\"https://petstore.swagger.io/v2/pet/findByStatus\",\"timestamp\":\"2023-01-05T04:20:13.328093384Z\",\"matcher-status\":false,\"matched-line\":null}\n" + + "{\"template-id\":\"swagger-version\",\"info\":{\"name\":\"Swagger 2.x Version Detection\",\"author\":[\"c-sh0\"],\"tags\":[\"tech\",\"swagger\",\"api\"],\"description\":\"Obtain Swagger Version\",\"reference\":[\"swagger ui 2.x and under\",\"https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/version-detection.md\"],\"severity\":\"info\"},\"type\":\"http\",\"host\":\"https://petstore.swagger.io/v2/pet/findByStatus\",\"timestamp\":\"2023-01-05T04:20:13.378927488Z\",\"matcher-status\":false,\"matched-line\":null}\n" + + "{\"template-id\":\"swagger-version\",\"info\":{\"name\":\"Swagger 2.x Version Detection\",\"author\":[\"c-sh0\"],\"tags\":[\"tech\",\"swagger\",\"api\"],\"description\":\"Obtain Swagger Version\",\"reference\":[\"swagger ui 2.x and under\",\"https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/version-detection.md\"],\"severity\":\"info\"},\"type\":\"http\",\"host\":\"https://petstore.swagger.io/v2/pet/findByStatus\",\"timestamp\":\"2023-01-05T04:20:13.429506909Z\",\"matcher-status\":false,\"matched-line\":null}\n" + + "{\"template-id\":\"swagger-version\",\"info\":{\"name\":\"Swagger 2.x Version Detection\",\"author\":[\"c-sh0\"],\"tags\":[\"tech\",\"swagger\",\"api\"],\"description\":\"Obtain Swagger Version\",\"reference\":[\"swagger ui 2.x and under\",\"https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/version-detection.md\"],\"severity\":\"info\"},\"type\":\"http\",\"host\":\"https://petstore.swagger.io/v2/pet/findByStatus\",\"timestamp\":\"2023-01-05T04:20:13.48052973Z\",\"matcher-status\":false,\"matched-line\":null}"; + } + + private String generateValidGmailCheck() { + return "id: valid-gmail-checker\n" + + "\n" + + "info:\n" + + " name: Valid Google Mail Checker\n" + + " author: dievus,dwisiswant0\n" + + " severity: info\n" + + " reference:\n" + + " - https://github.com/dievus/geeMailUserFinder\n" + + "\n" + + "self-contained: true\n" + + "requests:\n" + + " - method: HEAD\n" + + " path:\n" + + " - \"https://mail.google.com/mail/gxlu?email={{email}}\"\n" + + "\n" + + " matchers:\n" + + " - type: word\n" + + " part: header\n" + + " words:\n" + + " - \"COMPASS\""; + } + + private String generateWordpressPluginsDetect() { + return "id: wordpress-plugins-detect\n" + + "\n" + + "info:\n" + + " name: WordPress Plugins Detection\n" + + " author: 0xcrypto\n" + + " severity: info\n" + + " tags: fuzz,wordpress\n" + + "\n" + + "requests:\n" + + " - raw:\n" + + " - |\n" + + " GET /wp-content/plugins/{{pluginSlug}}/readme.txt HTTP/1.1\n" + + " Host: {{Hostname}}\n" + + "\n" + + " threads: 50\n" + + " payloads:\n" + + " pluginSlug: helpers/wordlists/wordpress-plugins.txt\n" + + "\n" + + " matchers-condition: and\n" + + " matchers:\n" + + " - type: status\n" + + " status:\n" + + " - 200\n" + + "\n" + + " - type: word\n" + + " words:\n" + + " - \"== Description ==\"\n" + + "\n" + + " extractors:\n" + + " - type: regex\n" + + " part: body\n" + + " group: 1\n" + + " regex:\n" + + " - \"===\\\\s(.*)\\\\s===\" # extract the plugin name\n" + + " - \"(?m)Stable tag: ([0-9.]+)\" # extract the plugin version"; + } + + + + private String generateSwaggerFileDetectionText() { + return "id: swagger-version\n" + + " \n" + + "info:\n" + + " name: Swagger 2.x Version Detection\n" + + " author: c-sh0\n" + + " reference:\n" + + " - Swagger UI 2.x and under\n" + + " - https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/version-detection.md\n" + + " severity: info\n" + + " description: Obtain Swagger Version\n" + + " tags: tech,swagger,api\n" + + "\n" + + "requests:\n" + + " - method: GET\n" + + " path:\n" + + " - \"{{BaseURL}}/{{locations}}\"\n" + + " payloads:\n" + + " locations: https://github.com/akto-api-security/testing_sources/blob/master/Misconfiguration/swagger-detection/wordlists/swagger_pathlist.txt\n" + + "\n" + + " matchers-condition: and\n" + + " matchers:\n" + + " - type: status\n" + + " status:\n" + + " - 200\n" + + "\n" + + " extractors:\n" + + " - type: regex\n" + + " part: body\n" + + " group: 1\n" + + " regex:\n" + + " - \" @version (v[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})\""; + } + +} diff --git a/apps/testing/src/test/java/com/akto/rules/TestTestPlugin.java b/apps/testing/src/test/java/com/akto/rules/TestTestPlugin.java new file mode 100644 index 0000000000..09634a80cc --- /dev/null +++ b/apps/testing/src/test/java/com/akto/rules/TestTestPlugin.java @@ -0,0 +1,280 @@ +package com.akto.rules; + +import com.akto.MongoBasedTest; +import com.akto.dao.context.Context; +import com.akto.dao.testing.TestingRunResultDao; +import com.akto.dto.*; +import com.akto.dto.testing.*; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods; +import com.akto.store.SampleMessageStore; +import com.akto.types.CappedSet; +import com.akto.util.JSONUtils; +import com.akto.util.modifier.NoneAlgoJWTModifier; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.sendgrid.Method; +import org.bson.types.ObjectId; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; + +public class TestTestPlugin extends MongoBasedTest { + + @Test + public void testIsStatusGood() { + boolean result = TestPlugin.isStatusGood(200); + assertTrue(result); + result = TestPlugin.isStatusGood(300); + assertFalse(result); + result = TestPlugin.isStatusGood(299); + assertTrue(result); + result = TestPlugin.isStatusGood(100); + assertFalse(result); + } + + @Test + public void testCompareWithOriginalResponse() { +// {"name": "Ankush", "age": 100, "friends": [{"name": "Avneesh", "stud": true}, {"name": "ankita", "stud": true}], "jobs": ["MS", "CT"]} + String originalPayload = "{\"name\": \"Ankush\", \"age\": 100, \"friends\": [{\"name\": \"Avneesh\", \"stud\": true}, {\"name\": \"ankita\", \"stud\": true}], \"jobs\": [\"MS\", \"CT\"]}"; + String currentPayload = "{\"name\": \"Vian\", \"age\": 1, \"friends\": [{\"name\": \"Avneesh\", \"stud\": true}, {\"name\": \"Ankita\", \"stud\": true}, {\"name\": \"Ankush\", \"stud\": true}], \"jobs\": []}"; + double val = TestPlugin.compareWithOriginalResponse(originalPayload, currentPayload, new HashMap<>()); + assertEquals(val,20.0, 0.0); + + // {"nestedObject": {"keyA":{"keyB":"B", "keyC": ["A", "B"]}}} + originalPayload = "{\"nestedObject\": {\"keyA\":{\"keyB\":\"B\", \"keyC\": [\"A\", \"B\"]}}}"; + currentPayload = "{\"nestedObject\": {\"keyA\":{\"keyB\":\"B\", \"keyC\": [\"A\"]}}}"; + val = TestPlugin.compareWithOriginalResponse(originalPayload, currentPayload, new HashMap<>()); + assertEquals(val,50.0, 0.0); + + // [{"name": "A", "age": 10},{"name": "B", "age": 10},{"name": "C", "age": 10}] + originalPayload = "[{\"name\": \"A\", \"age\": 10},{\"name\": \"B\", \"age\": 10},{\"name\": \"C\", \"age\": 10}]"; + currentPayload = "[{\"name\": \"B\", \"age\": 10},{\"name\": \"B\", \"age\": 10},{\"name\": \"C\", \"age\": 10}]"; + val = TestPlugin.compareWithOriginalResponse(originalPayload, currentPayload, new HashMap<>()); + assertEquals(val,50.0, 0.0); + + originalPayload = "{}"; + currentPayload = "{}"; + val = TestPlugin.compareWithOriginalResponse(originalPayload, currentPayload, new HashMap<>()); + assertEquals(val,100.0, 100.0); + + originalPayload = "{\"user\":{}}"; + currentPayload = "{\"user\":{}}"; + val = TestPlugin.compareWithOriginalResponse(originalPayload, currentPayload, new HashMap<>()); + assertEquals(val,100.0, 100.0); + } + + @Test + public void testCompareWithExcludedKeys() { +// {"name": "Ankush", "age": 100, "friends": [{"name": "Avneesh", "stud": true}, {"name": "ankita", "stud": true}], "jobs": ["MS", "CT"]} + String originalPayload = "{\"name\": \"Ankush\", \"age\": 100, \"friends\": [{\"name\": \"Avneesh\", \"stud\": true}, {\"name\": \"ankita\", \"stud\": true}], \"jobs\": [\"MS\", \"CT\"]}"; + String currentPayload = "{\"name\": \"Vian\", \"age\": 1, \"friends\": [{\"name\": \"Avneesh\", \"stud\": true}, {\"name\": \"Ankita\", \"stud\": true}, {\"name\": \"Ankush\", \"stud\": true}], \"jobs\": []}"; + Map excludedKeys = new HashMap<>(); + excludedKeys.put("friends#$#name", true); + excludedKeys.put("name", true); + double val = TestPlugin.compareWithOriginalResponse(originalPayload, currentPayload, excludedKeys); + assertEquals(val,33.3, 0.04); + } + + @Test + public void testGetExcludedKeys() { + ArrayList replayedResponses = new ArrayList<>(); + ArrayList>> replayedResponseMap = new ArrayList<>(); + String originalPayload1 = "{\"name\": \"Ankush\", \"age\": 100, \"friends\": [{\"name\": \"Avneesh\", \"stud\": true}, {\"name\": \"ankita\", \"stud\": true}], \"jobs\": [\"MS\", \"CT\"]}"; + String originalPayload2 = "{\"name\": \"Ankush\", \"age\": 101, \"friends\": [{\"name\": \"Avneesh\", \"stud\": false}, {\"name\": \"ankita\", \"stud\": true}]}"; + + Map> originalResponseParamMap1 = new HashMap<>(); + Map> originalResponseParamMap2 = new HashMap<>(); + try { + TestPlugin.extractAllValuesFromPayload(originalPayload1, originalResponseParamMap1); + TestPlugin.extractAllValuesFromPayload(originalPayload2, originalResponseParamMap2); + } catch(Exception e) { + fail("Unexpected Exception " + e.getMessage()); + } + replayedResponseMap.add(originalResponseParamMap1); + replayedResponseMap.add(originalResponseParamMap2); + replayedResponses.add(new OriginalHttpResponse(originalPayload1, new HashMap<>(), 200)); + replayedResponses.add(new OriginalHttpResponse(originalPayload2, new HashMap<>(), 200)); + SampleRequestReplayResponse sampleReplayResp = new SampleRequestReplayResponse(); + sampleReplayResp.setReplayedResponseMap(replayedResponseMap); + sampleReplayResp.setReplayedResponses(replayedResponses); + Map comparisonExcludedKeys = TestPlugin.getComparisonExcludedKeys(sampleReplayResp, sampleReplayResp.getReplayedResponseMap()); + assertEquals(comparisonExcludedKeys.size(), 2); + } + + @Test + public void testGetExcludedKeysOrderChanged() { + ArrayList replayedResponses = new ArrayList<>(); + ArrayList>> replayedResponseMap = new ArrayList<>(); + String originalPayload1 = "{\"name\": \"Ankush\", \"age\": 100, \"friends\": [{\"name\": \"ankita\", \"stud\": true}, {\"name\": \"Avneesh\", \"stud\": true}], \"jobs\": [\"MS\", \"CT\"]}"; + String originalPayload2 = "{\"name\": \"Ankush\", \"age\": 101, \"friends\": [{\"name\": \"Avneesh\", \"stud\": false}, {\"name\": \"ankita\", \"stud\": true}]}"; + + Map> originalResponseParamMap1 = new HashMap<>(); + Map> originalResponseParamMap2 = new HashMap<>(); + try { + TestPlugin.extractAllValuesFromPayload(originalPayload1, originalResponseParamMap1); + TestPlugin.extractAllValuesFromPayload(originalPayload2, originalResponseParamMap2); + } catch(Exception e) { + fail("Unexpected Exception " + e.getMessage()); + } + replayedResponseMap.add(originalResponseParamMap1); + replayedResponseMap.add(originalResponseParamMap2); + replayedResponses.add(new OriginalHttpResponse(originalPayload1, new HashMap<>(), 200)); + replayedResponses.add(new OriginalHttpResponse(originalPayload2, new HashMap<>(), 200)); + SampleRequestReplayResponse sampleReplayResp = new SampleRequestReplayResponse(); + sampleReplayResp.setReplayedResponseMap(replayedResponseMap); + sampleReplayResp.setReplayedResponses(replayedResponses); + Map comparisonExcludedKeys = TestPlugin.getComparisonExcludedKeys(sampleReplayResp, sampleReplayResp.getReplayedResponseMap()); + assertEquals(comparisonExcludedKeys.size(), 2); + } + + @Test + public void testContainsPrivateResource() { + Map singleTypeInfoMap = new HashMap<>(); + BOLATest bolaTest = new BOLATest(); + + // FIRST (Contains only private resources) + ApiInfo.ApiInfoKey apiInfoKey1 = new ApiInfo.ApiInfoKey(123, "/api/books", URLMethods.Method.GET); + + insertIntoStiMap(apiInfoKey1,"param1", SingleTypeInfo.EMAIL, false, true, singleTypeInfoMap); + insertIntoStiMap(apiInfoKey1,"param2", SingleTypeInfo.GENERIC, false, true, singleTypeInfoMap); + insertIntoStiMap(apiInfoKey1,"param3", SingleTypeInfo.GENERIC, false, true, singleTypeInfoMap); + + String payload1 = "{\"param1\": \"avneesh@akto.io\", \"param2\": \"ankush\"}"; + OriginalHttpRequest originalHttpRequest1 = new OriginalHttpRequest("/api/books", "param3=ankita", apiInfoKey1.getMethod().name(), payload1, new HashMap<>(), ""); + TestPlugin.ContainsPrivateResourceResult result1 = bolaTest.containsPrivateResource(originalHttpRequest1, apiInfoKey1, singleTypeInfoMap); + assertEquals(3, result1.singleTypeInfos.size()); + assertEquals(3, result1.findPrivateOnes().size()); + assertTrue(result1.isPrivate); + + // SECOND (Contains 2 public resources) + ApiInfo.ApiInfoKey apiInfoKey2 = new ApiInfo.ApiInfoKey(123, "api/INTEGER/cars/STRING", URLMethods.Method.GET); + + insertIntoStiMap(apiInfoKey2,"1", SingleTypeInfo.INTEGER_32, true, true, singleTypeInfoMap); + insertIntoStiMap(apiInfoKey2,"3", SingleTypeInfo.GENERIC, true,false, singleTypeInfoMap); + insertIntoStiMap(apiInfoKey2,"param1", SingleTypeInfo.GENERIC, false, true, singleTypeInfoMap); + insertIntoStiMap(apiInfoKey2,"param2", SingleTypeInfo.GENERIC, false,false, singleTypeInfoMap); + String payload2 = "{\"param1\": \"Ronaldo\", \"param2\": \"Messi\"}"; + OriginalHttpRequest originalHttpRequest2 = new OriginalHttpRequest("/api/INTEGER/cars/STRING", null ,apiInfoKey2.getMethod().name(), payload2, new HashMap<>(), ""); + TestPlugin.ContainsPrivateResourceResult result2 = bolaTest.containsPrivateResource(originalHttpRequest2, apiInfoKey2, singleTypeInfoMap); + assertEquals(4, result2.singleTypeInfos.size()); + assertFalse(result2.isPrivate); + assertEquals(2, result2.findPrivateOnes().size()); + + // THIRD (All missing) [We give missing STI benefit of doubt and consider it to be private] + ApiInfo.ApiInfoKey apiInfoKey3 = new ApiInfo.ApiInfoKey(123, "/api/bus", URLMethods.Method.GET); + + String payload3 = "{\"param1\": \"Ronaldo\", \"param2\": \"Messi\"}"; + OriginalHttpRequest originalHttpRequest3 = new OriginalHttpRequest("/api/bus", null, apiInfoKey3.method.name(), payload3, new HashMap<>(), ""); + TestPlugin.ContainsPrivateResourceResult result3 = bolaTest.containsPrivateResource(originalHttpRequest3, apiInfoKey3, singleTypeInfoMap); + assertEquals(0, result3.singleTypeInfos.size()); + assertTrue(result3.isPrivate); + assertEquals(0, result3.findPrivateOnes().size()); + + // FOURTH (Empty payload) + ApiInfo.ApiInfoKey apiInfoKey4 = new ApiInfo.ApiInfoKey(123, "/api/toys", URLMethods.Method.GET); + + String payload4 = "{}"; + OriginalHttpRequest originalHttpRequest4 = new OriginalHttpRequest("/api/toys",null, apiInfoKey4.getMethod().name(), payload4, new HashMap<>(), ""); + TestPlugin.ContainsPrivateResourceResult result4 = bolaTest.containsPrivateResource(originalHttpRequest4, apiInfoKey4, singleTypeInfoMap); + assertEquals(0, result4.singleTypeInfos.size()); + assertFalse(result4.isPrivate); + assertEquals(0, result4.findPrivateOnes().size()); + + } + + private HttpRequestParams buildHttpReq(String url, String method, int apiCollectionId, String payload) { + return new HttpRequestParams( + method, url, "", new HashMap<>(), payload, apiCollectionId + ); + } + + private void insertIntoStiMap(ApiInfo.ApiInfoKey apiInfoKey, String param, SingleTypeInfo.SubType subType, + boolean isUrlParam, boolean isPrivate, Map singleTypeInfoMap) { + int apiCollectionId = apiInfoKey.getApiCollectionId(); + String url = apiInfoKey.getUrl(); + String method = apiInfoKey.getMethod().name(); + + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + url, method,-1, false, param, subType, apiCollectionId, isUrlParam + ); + + SingleTypeInfo singleTypeInfo = new SingleTypeInfo( + paramId,new HashSet<>(), new HashSet<>(), 0, Context.now(), 0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE + ); + + singleTypeInfo.setPublicCount(10); + if (isPrivate) { + singleTypeInfo.setUniqueCount(1000000); + } else { + singleTypeInfo.setUniqueCount(10); + } + + singleTypeInfoMap.put(singleTypeInfo.composeKeyWithCustomSubType(SingleTypeInfo.GENERIC), singleTypeInfo); + } + + + @Test + public void testDecrementUrlVersion() { + String result = TestPlugin.decrementUrlVersion("/api/v2/books/v3n0m/", 1, 1); + assertEquals("/api/v1/books/v3n0m/", result); + + result = TestPlugin.decrementUrlVersion("/api/v22/books/v3/cars", 2, 1); + assertEquals("/api/v20/books/v1/cars", result); + + result = TestPlugin.decrementUrlVersion("/api/v22/books/", 2, 1); + assertEquals("/api/v20/books/", result); + + result = TestPlugin.decrementUrlVersion("/api/v22/books", -1, 1); + assertEquals("/api/v23/books", result); + + result = TestPlugin.decrementUrlVersion("/api/v1/books", 1, 1); + assertNull(result); + + } + + @Test + public void testModifyJwtHeaderToNoneAlgo() { + + Map> headers = new HashMap<>(); + headers.put("origin", Collections.singletonList("https://www.akto.io")); + headers.put("random", Collections.singletonList("avneesh_is_studddd")); + + // no change to headers since it doesn't find any JWT token + Map> result = JSONUtils.modifyHeaderValues(headers, new NoneAlgoJWTModifier()); + assertNull(result); + assertEquals(2, headers.size()); + assertEquals("https://www.akto.io", headers.get("origin").get(0)); + assertEquals("avneesh_is_studddd", headers.get("random").get(0)); + + headers.put("access-token", Collections.singletonList("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c")); + result = JSONUtils.modifyHeaderValues(headers, new NoneAlgoJWTModifier()); + assertNotNull(result); + assertEquals("eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.", result.get("access-token").get(0)); + } + + @Test + public void testFindUndocumentedMethods() { + Map> sampleMessages = new HashMap<>(); + sampleMessages.put(new ApiInfo.ApiInfoKey(0, "/api/books", URLMethods.Method.GET), Collections.singletonList("")); + sampleMessages.put(new ApiInfo.ApiInfoKey(0, "/api/books", URLMethods.Method.POST), Collections.singletonList("")); + sampleMessages.put(new ApiInfo.ApiInfoKey(1, "/api/books", URLMethods.Method.PUT), Collections.singletonList("")); + sampleMessages.put(new ApiInfo.ApiInfoKey(0, "/api/books/INTEGER", URLMethods.Method.GET), Collections.singletonList("")); + sampleMessages.put(new ApiInfo.ApiInfoKey(0, "/api/books/INTEGER", URLMethods.Method.POST), Collections.singletonList("")); + sampleMessages.put(new ApiInfo.ApiInfoKey(1, "/api/books/INTEGER", URLMethods.Method.PUT), Collections.singletonList("")); + + ApiInfo.ApiInfoKey apiInfoKey = new ApiInfo.ApiInfoKey(0, "/api/books", URLMethods.Method.GET); + List undocumentedMethods = TestPlugin.findUndocumentedMethods(sampleMessages, apiInfoKey); + assertEquals(3, undocumentedMethods.size()); + + apiInfoKey = new ApiInfo.ApiInfoKey(0, "/api/books/1", URLMethods.Method.GET); + undocumentedMethods = TestPlugin.findUndocumentedMethods(sampleMessages, apiInfoKey); + assertEquals(3, undocumentedMethods.size()); + + } + + + +} diff --git a/apps/testing/src/test/java/com/akto/store/TestSampleMessageStore.java b/apps/testing/src/test/java/com/akto/store/TestSampleMessageStore.java new file mode 100644 index 0000000000..82e7246ba8 --- /dev/null +++ b/apps/testing/src/test/java/com/akto/store/TestSampleMessageStore.java @@ -0,0 +1,74 @@ +package com.akto.store; + +import com.akto.MongoBasedTest; +import com.akto.dao.SampleDataDao; +import com.akto.dao.testing.TestingRunResultDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.RawApi; +import com.akto.dto.testing.*; +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.URLMethods; +import com.akto.rules.BOLATest; +import com.mongodb.client.model.Filters; +import org.bson.types.ObjectId; +import org.junit.Test; +import org.springframework.security.core.parameters.P; + +import java.util.*; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class TestSampleMessageStore extends MongoBasedTest { + + @Test + public void testFetchSampleMessages() { + SampleDataDao.instance.getMCollection().drop(); + SampleData sampleData1 = new SampleData(new Key(0, "url1", URLMethods.Method.GET,0,0,0), null); + SampleData sampleData2 = new SampleData(new Key(0, "url2", URLMethods.Method.GET,0,0,0), Arrays.asList("m1", "m2")); + SampleData sampleData3 = new SampleData(new Key(0, "url3", URLMethods.Method.GET,0,0,0), Collections.emptyList()); + SampleDataDao.instance.insertMany(Arrays.asList(sampleData1, sampleData2, sampleData3)); + + Map> sampleDataMap = SampleMessageStore.fetchSampleMessages(); + + assertEquals(sampleDataMap.size(), 2); + List messages = sampleDataMap.get(new ApiInfo.ApiInfoKey(0, "url2", URLMethods.Method.GET)); + assertEquals(messages.size(), 2); + + SampleDataDao.instance.getMCollection().drop(); + sampleData2 = new SampleData(new Key(0, "url2", URLMethods.Method.GET,0,0,0), Arrays.asList("m1", "m2", "m3")); + SampleDataDao.instance.insertMany(Arrays.asList(sampleData1, sampleData2)); + + sampleDataMap = SampleMessageStore.fetchSampleMessages(); + assertEquals(sampleDataMap.size(), 1); + messages = sampleDataMap.get(new ApiInfo.ApiInfoKey(0, "url2", URLMethods.Method.GET)); + assertEquals(messages.size(), 3); + + } + + @Test + public void testFilterMessagesWithAuthToken() { + AuthMechanism authMechanism = new AuthMechanism( + Collections.singletonList(new HardcodedAuthParam(AuthParam.Location.HEADER, "akto", "something", true)), null, null + ); + + List filteredList = SampleMessageStore.filterMessagesWithAuthToken(new ArrayList<>() , authMechanism); + assertEquals(0, filteredList.size()); + + List values = new ArrayList<>(); + // both values don't contain auth token + values.add(RawApi.buildFromMessage("{ \"method\": \"GET\", \"requestPayload\": \"{}\", \"responsePayload\": \"{\\\"id\\\":2,\\\"category\\\":{\\\"id\\\":0},\\\"name\\\":\\\"teste\\\",\\\"photoUrls\\\":[],\\\"tags\\\":[]}\", \"ip\": \"null\", \"source\": \"HAR\", \"type\": \"HTTP/2\", \"akto_vxlan_id\": \"1661807253\", \"path\": \"https://petstore.swagger.io/v2/pet/2\", \"requestHeaders\": \"{\\\"TE\\\":\\\"trailers\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\", \\\"Origin\\\" : \\\"dddd\\\"}\", \"responseHeaders\": \"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:12:27 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\", \"time\": \"1641327147\", \"contentType\": \"application/json\", \"akto_account_id\": \"1000000\", \"statusCode\": \"200\", \"status\": \"OK\" }")); + values.add(RawApi.buildFromMessage("{ \"method\": \"GET\", \"requestPayload\": \"{}\", \"responsePayload\": \"{\\\"id\\\":2,\\\"category\\\":{\\\"id\\\":0},\\\"name\\\":\\\"teste\\\",\\\"photoUrls\\\":[],\\\"tags\\\":[]}\", \"ip\": \"null\", \"source\": \"HAR\", \"type\": \"HTTP/2\", \"akto_vxlan_id\": \"1661807253\", \"path\": \"https://petstore.swagger.io/v2/pet/2\", \"requestHeaders\": \"{\\\"TE\\\":\\\"trailers\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\", \\\"Origin\\\" : \\\"dddd\\\"}\", \"responseHeaders\": \"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:12:27 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\", \"time\": \"1641327147\", \"contentType\": \"application/json\", \"akto_account_id\": \"1000000\", \"statusCode\": \"200\", \"status\": \"OK\" }")); + + filteredList = SampleMessageStore.filterMessagesWithAuthToken(values, authMechanism); + assertEquals(0, filteredList.size()); + + // this value contains auth token so no errors + values.add(RawApi.buildFromMessage("{ \"method\": \"GET\", \"requestPayload\": \"{}\", \"responsePayload\": \"{\\\"id\\\":2,\\\"category\\\":{\\\"id\\\":0},\\\"name\\\":\\\"teste\\\",\\\"photoUrls\\\":[],\\\"tags\\\":[]}\", \"ip\": \"null\", \"source\": \"HAR\", \"type\": \"HTTP/2\", \"akto_vxlan_id\": \"1661807253\", \"path\": \"https://petstore.swagger.io/v2/pet/2\", \"requestHeaders\": \"{\\\"TE\\\":\\\"trailers\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\", \\\"Origin\\\" : \\\"dddd\\\", \\\"akto\\\" : \\\"blah\\\"}\", \"responseHeaders\": \"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:12:27 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\", \"time\": \"1641327147\", \"contentType\": \"application/json\", \"akto_account_id\": \"1000000\", \"statusCode\": \"200\", \"status\": \"OK\" }")); + + filteredList = SampleMessageStore.filterMessagesWithAuthToken(values, authMechanism); + assertEquals(1, filteredList.size()); + + } +} diff --git a/apps/testing/src/test/java/com/akto/testing/ApiExecutorTest.java b/apps/testing/src/test/java/com/akto/testing/ApiExecutorTest.java new file mode 100644 index 0000000000..d1a9db5de4 --- /dev/null +++ b/apps/testing/src/test/java/com/akto/testing/ApiExecutorTest.java @@ -0,0 +1,38 @@ +package com.akto.testing; + +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.type.RequestTemplate; +import com.akto.runtime.URLAggregator; +import com.mongodb.BasicDBObject; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class ApiExecutorTest { + + @Test + public void testMakeUrlAbsolute() throws Exception { + String originalUrl = "/dashboard"; + String url = OriginalHttpRequest.makeUrlAbsolute(originalUrl, "akto.io", "https"); + assertEquals(url, "https://akto.io/dashboard"); + + originalUrl = "dashboard"; + url = OriginalHttpRequest.makeUrlAbsolute(originalUrl, "akto.io", "http"); + assertEquals(url, "http://akto.io/dashboard"); + + originalUrl = "/dashboard"; + url = OriginalHttpRequest.makeUrlAbsolute(originalUrl, "https://www.akto.io/", null); + assertEquals(url, "https://www.akto.io/dashboard"); + + originalUrl = "/dashboard"; + url = OriginalHttpRequest.makeUrlAbsolute(originalUrl, "akto.io/", null); + assertEquals(url, "https://akto.io/dashboard"); + + originalUrl = "/dashboard"; + url = OriginalHttpRequest.makeUrlAbsolute(originalUrl, "127.0.0.1", null ); + assertEquals(url, "http://127.0.0.1/dashboard"); + } + +} diff --git a/apps/testing/src/test/java/com/akto/testing/ApiWorkflowExecutorTest.java b/apps/testing/src/test/java/com/akto/testing/ApiWorkflowExecutorTest.java new file mode 100644 index 0000000000..7b6e805b03 --- /dev/null +++ b/apps/testing/src/test/java/com/akto/testing/ApiWorkflowExecutorTest.java @@ -0,0 +1,299 @@ +package com.akto.testing; + +import com.akto.MongoBasedTest; +import com.akto.dao.OtpTestDataDao; +import com.akto.dao.context.Context; +import com.akto.dao.testing.LoginFlowStepsDao; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.api_workflow.Node; +import com.akto.dto.testing.LoginFlowParams; +import com.akto.dto.testing.LoginFlowStepsData; +import com.akto.dto.testing.WorkflowNodeDetails; +import com.akto.dto.testing.WorkflowUpdatedSampleData; +import com.akto.dto.testing.WorkflowTestResult.NodeResult; +import com.akto.dto.type.RequestTemplate; +import com.akto.runtime.URLAggregator; +import com.akto.types.BasicDBListL; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; + +import org.bson.conversions.Bson; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; + +public class ApiWorkflowExecutorTest extends MongoBasedTest { + + + @Test + public void testCombineQueryParams2() { + String query1 = ""; + String query2 = "blah"; + String combinedQuery = OriginalHttpRequest.combineQueryParams(query1, query2); + assertEquals("blah", combinedQuery); + } + + @Test + public void testExecuteCode() throws Exception { + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + Map valuesMap = new HashMap<>(); + valuesMap.put("x1.response.body.user.name", "avneesh"); + valuesMap.put("x1.response.body.user.age", 99); + + String payload = "{\"user_name\": \"${x1.response.body.user.name}\", \"user_age\": ${x1.response.body.user.age}}"; + String result = apiWorkflowExecutor.executeCode(payload, valuesMap); + assertEquals("{\"user_name\": \"avneesh\", \"user_age\": 99}", result); + + + payload = "{\"user_name\": '#[\"${x1.response.body.user.name}\".toUpperCase()]#', \"user_age\": #[${x1.response.body.user.age} + 1]#}"; + result = apiWorkflowExecutor.executeCode(payload, valuesMap); + assertEquals("{\"user_name\": 'AVNEESH', \"user_age\": 100}", result); + + valuesMap.put("x1.response.body.url", "https://api.razorpay.com:443/v1/payments/pay_K6FMfsnyloigxs/callback/941349c12d0e001436ace03ee711367413b176bb/rzp_test_1DP5mmOlF5G5ag"); + + String urlPayload = "#[ var a = '${x1.response.body.url}'; var b = a.split('/'); b[5] = 'avneesh'; b.join('/'); ]#"; + result = apiWorkflowExecutor.executeCode(urlPayload, valuesMap); + assertEquals("https://api.razorpay.com:443/v1/payments/avneesh/callback/941349c12d0e001436ace03ee711367413b176bb/rzp_test_1DP5mmOlF5G5ag", result); + + urlPayload = "#[ '${x1.response.body.url}'.replace(new RegExp('pay_.*?/'), 'avneesh/') ]#"; + result = apiWorkflowExecutor.executeCode(urlPayload, valuesMap); + assertEquals("https://api.razorpay.com:443/v1/payments/avneesh/callback/941349c12d0e001436ace03ee711367413b176bb/rzp_test_1DP5mmOlF5G5ag", result); + + valuesMap.put("x2.response.body.sentence", "This is sentence with a 'random' quote"); + payload = "#[ '${x2.response.body.sentence}'.replace(new RegExp('random'), 'avneesh') ]#"; + result = apiWorkflowExecutor.executeCode(payload, valuesMap); + assertEquals("This is sentence with a 'avneesh' quote", result); + + } + + @Test + public void testPopulateValuesMap() { + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + Map valuesMap = new HashMap<>(); + String payload = "{\"users\": [{\"name\": \"avneesh\", \"age\": 99}, {\"name\": \"ankush\", \"age\": 999} ], \"total\": 2, \"CEO\": {\"name\": \"Ankita\"}}"; + String nodeId = "x1"; + Map> headers = new HashMap<>(); + String queryParams = "status=online"; + apiWorkflowExecutor.populateValuesMap(valuesMap, payload, nodeId, headers, true, queryParams); + + assertEquals(8, valuesMap.size()); // 7 normal values + entire body string + assertEquals("online", valuesMap.get("x1.request.query.status")); + assertEquals("avneesh", valuesMap.get("x1.request.body.users[0].name")); + assertEquals(99, valuesMap.get("x1.request.body.users[0].age")); + assertEquals("ankush", valuesMap.get("x1.request.body.users[1].name")); + assertEquals(999, valuesMap.get("x1.request.body.users[1].age")); + assertEquals(2, valuesMap.get("x1.request.body.total")); + assertEquals("Ankita", valuesMap.get("x1.request.body.CEO.name")); + + payload = "{\"company\": \"Akto\"}"; + headers.put("x-forwarded-for", Arrays.asList("ip1", "ip2")); + apiWorkflowExecutor.populateValuesMap(valuesMap, payload, nodeId, headers, false, null); + + assertEquals(11, valuesMap.size()); // 8 from earlier + 2 values + 1 body string (this time for isRequest false) + assertEquals("Akto", valuesMap.get("x1.response.body.company")); + assertEquals("ip1;ip2", valuesMap.get("x1.response.header.x-forwarded-for")); + + + payload = "mobNo=999999999&Vehicle=Car"; + apiWorkflowExecutor.populateValuesMap(valuesMap, payload, nodeId, new HashMap<>(),true, null); + assertEquals(13, valuesMap.size()); // 11 + 2 new (no request.body because already filled) + assertEquals("999999999", valuesMap.get("x1.request.body.mobNo")); + assertEquals("Car", valuesMap.get("x1.request.body.Vehicle")); + } + + @Test + public void testBuildHttpRequest() throws Exception { + String orig = "{\"method\":\"GET\",\"requestPayload\":\"{}\",\"responsePayload\":\"{\\\"sold\\\":7,\\\"string\\\":256,\\\"connector\\\":1,\\\"pending\\\":5,\\\"available\\\":679,\\\"verified\\\":1}\",\"ip\":\"null\",\"source\":\"HAR\",\"type\":\"HTTP/2\",\"akto_vxlan_id\":\"1661807253\",\"path\":\"https://petstore.swagger.io/v2/store/inventory?random=hehe\",\"requestHeaders\":\"{\\\"TE\\\":\\\"trailers\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:11:27 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\",\"time\":\"1641327087\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + String queryParams = "user=avneesh"; + String requestPayload = "{\"initials\": \"AH\"}"; + String requestUrl = "https://stud.akto.io/stud_${x1.response.body.id}"; + WorkflowUpdatedSampleData workflowUpdatedSampleData = new WorkflowUpdatedSampleData( + orig, queryParams, null, requestPayload, requestUrl + ); + + Map valuesMap = new HashMap<>(); + valuesMap.put("x1.response.body.id", 10); + + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + OriginalHttpRequest originalHttpRequest = apiWorkflowExecutor.buildHttpRequest(workflowUpdatedSampleData, valuesMap); + + assertEquals(originalHttpRequest.getQueryParams(), "user=avneesh"); + assertEquals(originalHttpRequest.getHeaders().size(), 11); + assertEquals(originalHttpRequest.getBody(), requestPayload); + assertEquals(originalHttpRequest.getUrl(), "https://stud.akto.io/stud_10"); + } + + + @Test + public void testValidateTest() { + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + Map valuesMap = new HashMap<>(); + + String testValidatorCode = "${x1.response.status_code} === 200"; + valuesMap.put("x1.response.status_code", 401); + boolean vulnerable = apiWorkflowExecutor.validateTest(testValidatorCode, valuesMap); + assertTrue(vulnerable); + + testValidatorCode = "'${x1.request.body.user_name}' === '${x1.response.body.user_name}'"; + valuesMap.put("x1.request.body.user_name", "Avneesh"); + valuesMap.put("x1.response.body.user_name", "Ankush"); + vulnerable = apiWorkflowExecutor.validateTest(testValidatorCode, valuesMap); + assertTrue(vulnerable); + + testValidatorCode = "'${x1.request.body.CTO}' === 'Ankush'"; + valuesMap.put("x1.request.body.CTO", "Ankush"); + vulnerable = apiWorkflowExecutor.validateTest(testValidatorCode, valuesMap); + assertFalse(vulnerable); + + String p = "Razorpay - Payment in progress

Payment
Redirecting...
Click here to proceed
"; + testValidatorCode = "'${x1.response.body}'.indexOf('\"error\"') < 0"; + valuesMap.put("x1.response.body", p); + vulnerable = apiWorkflowExecutor.validateTest(testValidatorCode, valuesMap); + assertTrue(vulnerable); + } + + private BasicDBObject generateValue(String host, String endpoint, String method) { + BasicDBObject value = new BasicDBObject(); + value.put("host", host); + value.put("url", endpoint); + value.put("method", method); + return value; + } + + @Test + public void testReplaceVariables() throws Exception { + Map valuesMap = new HashMap<>(); + + List newSensitiveEndpoints = new ArrayList<>(); + newSensitiveEndpoints.add(generateValue("host1", "endpoint1", "GET")); + newSensitiveEndpoints.add(generateValue("host2", "endpoint2", "GET")); + newSensitiveEndpoints.add(generateValue("host3", "endpoint3", "POST")); + + List newEndpoints = new ArrayList<>(); + newEndpoints.add(generateValue("host4", "endpoint1", "GET")); + newEndpoints.add(generateValue("host5", "endpoint2", "GET")); + + valuesMap.put("AKTO.changes_info.newSensitiveEndpoints", newSensitiveEndpoints); + valuesMap.put("AKTO.changes_info.newSensitiveEndpointsCount", 139); + valuesMap.put("AKTO.changes_info.newEndpoints", newEndpoints); + valuesMap.put("AKTO.changes_info.newEndpointsCount", 2088); + valuesMap.put("AKTO.changes_info.newSensitiveParametersCount", 305); + valuesMap.put("AKTO.changes_info.newParametersCount", 0); + + String body = "{" + + "\"newSensitiveEndpoints\" : ${AKTO.changes_info.newSensitiveEndpoints}," + + " \"newSensitiveEndpointsCount\" : ${AKTO.changes_info.newSensitiveEndpointsCount}," + + " \"newEndpoints\" : ${AKTO.changes_info.newEndpoints}," + + " \"newEndpointsCount\" : ${AKTO.changes_info.newEndpointsCount}," + + " \"newSensitiveParametersCount\" : ${AKTO.changes_info.newSensitiveParametersCount}," + + " \"newParametersCount\" : ${AKTO.changes_info.newParametersCount}" + + "}"; + + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + String payload = apiWorkflowExecutor.replaceVariables(body, valuesMap, false); + + BasicDBObject payloadObject = BasicDBObject.parse(payload); + + assertEquals( 139 ,payloadObject.get("newSensitiveEndpointsCount")); + assertEquals( 2088 ,payloadObject.get("newEndpointsCount")); + assertEquals( 305,payloadObject.get("newSensitiveParametersCount")); + assertEquals( 0,payloadObject.get("newParametersCount")); + + BasicDBList basicDBList = (BasicDBList) payloadObject.get("newEndpoints"); + assertEquals(2, basicDBList.size()); + + basicDBList = (BasicDBList) payloadObject.get("newSensitiveEndpoints"); + assertEquals(3, basicDBList.size()); + + } + + @Test + public void testConstructValueMapWithLoginParams() { + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + Map valuemap = apiWorkflowExecutor.constructValueMap(null); + assertEquals(0, valuemap.size()); + } + + @Test + public void testConstructValueMapFetchValueMapFalse() { + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + Map valuemap = apiWorkflowExecutor.constructValueMap(new LoginFlowParams(23, false, "x1")); + assertEquals(0, valuemap.size()); + } + + @Test + public void testProcessOtpNodeOtpDataMissing() { + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + WorkflowNodeDetails nodeDetails = new WorkflowNodeDetails(0, "", null, "", null, null, false, + 0, 0, 0, "(/d+){1,6}", "123"); + Map valuemap = new HashMap<>(); + Node node = new Node("x1", nodeDetails); + NodeResult result = apiWorkflowExecutor.processOtpNode(node, valuemap); + assertEquals("{\"response\": {\"body\": {\"error\": \"otp data not received for uuid 123\"}}}", result.getMessage()); + } + + @Test + public void testProcessOtpNodeDataStaleData() { + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + WorkflowNodeDetails nodeDetails = new WorkflowNodeDetails(0, "", null, "", null, null, false, + 0, 0, 0, "(/d+){1,6}", "123"); + Map valuemap = new HashMap<>(); + Node node = new Node("x1", nodeDetails); + + int curTime = Context.now() - 10 * 60; + Bson updates = Updates.combine( + Updates.set("otpText", "Your otp is 123456"), + Updates.set("createdAtEpoch", curTime) + ); + + OtpTestDataDao.instance.updateOne(Filters.eq("uuid", "123"), updates); + + NodeResult result = apiWorkflowExecutor.processOtpNode(node, valuemap); + assertEquals("{\"response\": {\"body\": {\"error\": \"otp data not received for uuid 123\"}}}", result.getMessage()); + } + + @Test + public void testProcessOtpNodeDataInvalidRegex() { + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + WorkflowNodeDetails nodeDetails = new WorkflowNodeDetails(0, "", null, "", null, null, false, + 0, 0, 0, "(/d+){1,6}", "124"); + Map valuemap = new HashMap<>(); + Node node = new Node("x1", nodeDetails); + + int curTime = Context.now(); + Bson updates = Updates.combine( + Updates.set("otpText", "Your otp is 123456"), + Updates.set("createdAtEpoch", curTime) + ); + + OtpTestDataDao.instance.updateOne(Filters.eq("uuid", "124"), updates); + + NodeResult result = apiWorkflowExecutor.processOtpNode(node, valuemap); + assertEquals("{\"response\": {\"body\": {\"error\": \"unable to extract otp for provided regex\"}}}", result.getMessage()); + } + + @Test + public void testProcessOtpNodeData() { + ApiWorkflowExecutor apiWorkflowExecutor = new ApiWorkflowExecutor(); + WorkflowNodeDetails nodeDetails = new WorkflowNodeDetails(0, "", null, "", null, null, false, + 0, 0, 0, "(\\d+){1,6}", "125"); + Map valuemap = new HashMap<>(); + Node node = new Node("x1", nodeDetails); + + int curTime = Context.now(); + Bson updates = Updates.combine( + Updates.set("otpText", "Your otp is 123456"), + Updates.set("createdAtEpoch", curTime) + ); + + OtpTestDataDao.instance.updateOne(Filters.eq("uuid", "125"), updates); + + NodeResult result = apiWorkflowExecutor.processOtpNode(node, valuemap); + assertEquals(0, result.getErrors().size()); + assertEquals("{\"response\": {\"body\": {\"otp\": \"123456\", \"otpText\": \"Your otp is 123456\"}}}", result.getMessage()); + } +} diff --git a/apps/testing/src/test/java/com/akto/testing/StatusCodeAnalyserTest.java b/apps/testing/src/test/java/com/akto/testing/StatusCodeAnalyserTest.java new file mode 100644 index 0000000000..a636c495ca --- /dev/null +++ b/apps/testing/src/test/java/com/akto/testing/StatusCodeAnalyserTest.java @@ -0,0 +1,58 @@ +package com.akto.testing; + +import com.akto.dto.ApiInfo; +import com.akto.dto.HttpRequestParams; +import com.akto.dto.HttpResponseParams; +import com.akto.store.SampleMessageStore; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class StatusCodeAnalyserTest { + + @Test + public void testPotentialStatusCodeKeys() { + Map> responseParamMap = new HashMap<>(); + responseParamMap.put("something", null); + responseParamMap.put("status#reason", Collections.singleton(null)); + responseParamMap.put("status#code", Collections.singleton("200")); + responseParamMap.put("status#message", Collections.singleton("message")); + responseParamMap.put("status#type", Collections.singleton("type")); + responseParamMap.put("status#title", Collections.singleton("title")); + + List potentialStatusCodeKeys = StatusCodeAnalyser.getPotentialStatusCodeKeys(responseParamMap); + assertEquals(potentialStatusCodeKeys.size(), 1); + + responseParamMap.put("status#type", Collections.singleton("300")); + potentialStatusCodeKeys = StatusCodeAnalyser.getPotentialStatusCodeKeys(responseParamMap); + assertEquals(potentialStatusCodeKeys.size(), 1); + + responseParamMap.put("status#code", Collections.singleton("2000")); + responseParamMap.put("status#type", Collections.singleton("type")); + potentialStatusCodeKeys = StatusCodeAnalyser.getPotentialStatusCodeKeys(responseParamMap); + assertEquals(potentialStatusCodeKeys.size(), 0); + } + + + @Test + public void testGetStatusCode() { + StatusCodeAnalyser.result = new ArrayList<>(); + StatusCodeAnalyser.result.add(new StatusCodeAnalyser.StatusCodeIdentifier(new HashSet<>(Arrays.asList("status#code", "status#reason", "status#message", "status#type", "status#title")), "status#code")); + + int statusCode = StatusCodeAnalyser.getStatusCode(null, 199); + assertEquals(statusCode, 199); + + statusCode = StatusCodeAnalyser.getStatusCode(null, 300); + assertEquals(statusCode, 300); + + String payload = "{\"status\":{\"code\":401,\"message\":\"UNAUTHORIZED\",\"reason\":\"\",\"type\":\"\",\"title\":\"\"}}"; + statusCode = StatusCodeAnalyser.getStatusCode(payload, 204); + assertEquals(statusCode,401); + + payload = "{\"status\":{\"code\":200,\"message\":\"OK\",\"reason\":\"\",\"type\":\"\",\"title\":\"\"},\"payload\":{\"kycIdType\":\"NONE\",\"kycStatus\":\"NOT_FOUND\",\"kycUploadedTime\":\"\",\"kycRejectionReason\":\"\",\"kycRemainingAttemptCount\":60,\"kycRequired\":false}}"; + statusCode = StatusCodeAnalyser.getStatusCode(payload, 204); + assertEquals(statusCode,200); + } +} diff --git a/apps/testing/src/test/java/com/akto/testing_issues/TestingIssuesHandlerTest.java b/apps/testing/src/test/java/com/akto/testing_issues/TestingIssuesHandlerTest.java new file mode 100644 index 0000000000..8798ee5a77 --- /dev/null +++ b/apps/testing/src/test/java/com/akto/testing_issues/TestingIssuesHandlerTest.java @@ -0,0 +1,98 @@ +package com.akto.testing_issues; + +import com.akto.MongoBasedTest; +import com.akto.dao.context.Context; +import com.akto.dao.testing_run_findings.TestingRunIssuesDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.test_run_findings.TestingRunIssues; +import com.akto.dto.testing.TestResult; +import com.akto.dto.testing.TestingRunResult; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods; +import com.akto.util.enums.GlobalEnums; +import com.mongodb.BasicDBObject; +import com.mongodb.client.model.Filters; +import org.bson.types.ObjectId; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Random; + +import static com.akto.util.Constants.ID; +import static org.junit.Assert.*; + +public class TestingIssuesHandlerTest extends MongoBasedTest { + + private static final String[] urls = new String[]{ + "url1" + }; + + private int getIndex (int length, Random random) { + return Math.abs(random.nextInt()) % length; + } + private TestingRunResult getTestingRunResult (ApiInfo.ApiInfoKey apiInfoKey, String testSubType, Random random) { + List ids = new ArrayList<>(); + ids.add(new ObjectId(new Date(System.currentTimeMillis()))); + ids.add(new ObjectId(new Date(System.currentTimeMillis() - 1000 * 60 * 60))); + ids.add(new ObjectId(new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 2))); + ObjectId summaryId = new ObjectId(); + + List results = new ArrayList<>(); + List singleTypeInfosList = new ArrayList<>(); + return new TestingRunResult(ids.get(getIndex(ids.size(),random)), + apiInfoKey, + "", + testSubType, + results, + true, + singleTypeInfosList, + 100, + Context.now(), + Context.now(), + summaryId); + } + + @Test + public void testHandler() { + TestingRunIssuesDao.instance.getMCollection().drop(); + List testingRunResultList = new ArrayList<>(); + Random random = new Random(); + for (int i = 0; i < 100; i++) { + int COLLECTION_ID = 123; + testingRunResultList.add(getTestingRunResult(new ApiInfo.ApiInfoKey(COLLECTION_ID, urls[getIndex(urls.length,random)], + URLMethods.Method.getValuesArray()[getIndex(URLMethods.Method.getValuesArray().length,random)]), + GlobalEnums.TestSubCategory.getValuesArray()[getIndex(GlobalEnums.TestSubCategory.getValuesArray().length, random)].getName(), random)); + } + + TestingIssuesHandler handler = new TestingIssuesHandler(); + handler.handleIssuesCreationFromTestingRunResults(testingRunResultList); + + TestingRunIssues issues = TestingRunIssuesDao.instance.findOne(new BasicDBObject()); + issues.setTestRunIssueStatus(GlobalEnums.TestRunIssueStatus.IGNORED); + TestingRunIssuesDao.instance.replaceOne(new BasicDBObject(ID, issues.getId()),issues); + + handler.handleIssuesCreationFromTestingRunResults(testingRunResultList); + + TestingRunIssues issuesReturned = TestingRunIssuesDao.instance.findOne(Filters.eq(ID, issues.getId())); + + assertEquals(GlobalEnums.TestRunIssueStatus.IGNORED, issuesReturned.getTestRunIssueStatus()); + + TestingRunResult runResult = testingRunResultList.get(5); + runResult.setVulnerable(false); + handler.handleIssuesCreationFromTestingRunResults(testingRunResultList); + + issues = TestingRunIssuesDao.instance.findOne(Filters.eq(TestingRunIssues.TEST_RUN_ISSUES_STATUS, GlobalEnums.TestRunIssueStatus.FIXED)); + assertNotNull(issues); + //When all said and done, total issue can't be more than 36 + + int size = TestingRunIssuesDao.instance.findAll(new BasicDBObject()).size(); + assertTrue(size <= + urls.length + * URLMethods.Method.getValuesArray().length + * GlobalEnums.TestSubCategory.getValuesArray().length + ); + + } +} diff --git a/libs/dao/.gitignore b/libs/dao/.gitignore new file mode 100644 index 0000000000..bf15d6f578 --- /dev/null +++ b/libs/dao/.gitignore @@ -0,0 +1,14 @@ +node_modules/ +package-lock.json +target/ +web/dist/ +yarn.lock +.DS_Store +*.iml +*.pem + +*.env +.settings +.project +.classpath +.idea/ diff --git a/libs/dao/README.md b/libs/dao/README.md new file mode 100644 index 0000000000..3e59322a15 --- /dev/null +++ b/libs/dao/README.md @@ -0,0 +1,2 @@ +Build using +` ~/Downloads/apache-maven-3.6.3/bin/mvn install` diff --git a/libs/dao/pom.xml b/libs/dao/pom.xml new file mode 100644 index 0000000000..23e5d59d4f --- /dev/null +++ b/libs/dao/pom.xml @@ -0,0 +1,122 @@ + + + 4.0.0 + + com.akto.libs + libs + ${revision} + + + com.akto.libs.dao + dao + jar + + + + + junit + junit + 4.13.1 + test + + + org.mongodb + mongodb-driver-sync + 4.2.1 + + + com.google.code.gson + gson + 2.8.9 + compile + + + org.slf4j + slf4j-simple + 1.7.32 + + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.json + json + 20211205 + + + + com.googlecode.libphonenumber + libphonenumber + 8.12.41 + + + commons-validator + commons-validator + 1.7 + + + io.swagger.parser.v3 + swagger-parser + 2.0.27 + + + + org.springframework.security + spring-security-web + 5.6.2 + + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + 3.2.6 + + + com.squareup.okhttp3 + okhttp + 4.9.3 + + + io.jsonwebtoken + jjwt-api + 0.11.2 + + + io.jsonwebtoken + jjwt-impl + 0.11.2 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.2 + runtime + + + com.akto.libs.integrations + integrations + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 8 + 8 + + + + src/main/java + src/test/java + + + diff --git a/libs/dao/src/main/java/com/akto/DaoInit.java b/libs/dao/src/main/java/com/akto/DaoInit.java new file mode 100644 index 0000000000..49553c26bb --- /dev/null +++ b/libs/dao/src/main/java/com/akto/DaoInit.java @@ -0,0 +1,220 @@ +package com.akto; + +import com.akto.dto.*; +import com.akto.dto.data_types.*; +import com.akto.dto.notifications.CustomWebhook; +import com.akto.dto.notifications.CustomWebhookResult; +import com.akto.dto.runtime_filters.FieldExistsFilter; +import com.akto.dto.FilterSampleData; +import com.akto.dto.runtime_filters.ResponseCodeRuntimeFilter; +import com.akto.dto.runtime_filters.RuntimeFilter; +import com.akto.dto.test_run_findings.TestingIssuesId; +import com.akto.dto.test_run_findings.TestingRunIssues; +import com.akto.dto.testing.*; +import com.akto.dto.testing.info.BFLATestInfo; +import com.akto.dto.testing.info.NucleiTestInfo; +import com.akto.dto.testing.info.TestInfo; +import com.akto.dto.testing.sources.TestSourceConfig; +import com.akto.dto.third_party_access.Credential; +import com.akto.dto.third_party_access.ThirdPartyAccess; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods.Method; +import com.akto.dto.type.URLTemplate; +import com.akto.types.CappedList; +import com.akto.types.CappedSet; +import com.akto.util.EnumCodec; +import com.akto.dto.Attempt.AttemptResult; +import com.akto.dto.auth.APIAuth; +import com.akto.util.enums.GlobalEnums; +import com.mongodb.ConnectionString; +import com.mongodb.MongoClientSettings; +import com.mongodb.client.MongoClients; + +import org.bson.codecs.configuration.CodecRegistries; +import org.bson.codecs.configuration.CodecRegistry; +import org.bson.codecs.pojo.ClassModel; +import org.bson.codecs.pojo.PojoCodecProvider; + +import static com.akto.dao.MCollection.clients; +import static org.bson.codecs.configuration.CodecRegistries.fromProviders; +import static org.bson.codecs.configuration.CodecRegistries.fromRegistries; + +public class DaoInit { + + public static void init(ConnectionString connectionString) { + ClassModel configClassModel = ClassModel.builder(Config.class).enableDiscriminator(true).build(); + ClassModel signupInfoClassModel = ClassModel.builder(SignupInfo.class).enableDiscriminator(true) + .build(); + ClassModel apiAuthClassModel = ClassModel.builder(APIAuth.class).enableDiscriminator(true).build(); + ClassModel attempResultModel = ClassModel.builder(AttemptResult.class).enableDiscriminator(true) + .build(); + ClassModel urlTemplateModel = ClassModel.builder(URLTemplate.class).enableDiscriminator(true) + .build(); + ClassModel pendingInviteCodeClassModel = ClassModel.builder(PendingInviteCode.class) + .enableDiscriminator(true).build(); + ClassModel rbacClassModel = ClassModel.builder(RBAC.class).enableDiscriminator(true).build(); + ClassModel singleTypeInfoClassModel = ClassModel.builder(SingleTypeInfo.class) + .enableDiscriminator(true).build(); + ClassModel kafkaHealthMetricClassModel = ClassModel.builder(KafkaHealthMetric.class) + .enableDiscriminator(true).build(); + ClassModel thirdPartyAccessClassModel = ClassModel.builder(ThirdPartyAccess.class) + .enableDiscriminator(true).build(); + ClassModel credentialClassModel = ClassModel.builder(Credential.class).enableDiscriminator(true) + .build(); + ClassModel apiTokenClassModel = ClassModel.builder(ApiToken.class).enableDiscriminator(true).build(); + ClassModel apiInfoClassModel = ClassModel.builder(ApiInfo.class).enableDiscriminator(true).build(); + ClassModel apiInfoKeyClassModel = ClassModel.builder(ApiInfo.ApiInfoKey.class) + .enableDiscriminator(true).build(); + ClassModel customFilterClassModel = ClassModel.builder(CustomFilter.class) + .enableDiscriminator(true).build(); + ClassModel fieldExistsFilterClassModel = ClassModel.builder(FieldExistsFilter.class) + .enableDiscriminator(true).build(); + ClassModel responseCodeRuntimeFilterClassModel = ClassModel + .builder(ResponseCodeRuntimeFilter.class).enableDiscriminator(true).build(); + ; + ClassModel runtimeFilterClassModel = ClassModel.builder(RuntimeFilter.class) + .enableDiscriminator(true).build(); + ClassModel filterSampleDataClassModel = ClassModel.builder(FilterSampleData.class) + .enableDiscriminator(true).build(); + ClassModel accountSettingsClassModel = ClassModel.builder(AccountSettings.class) + .enableDiscriminator(true).build(); + ClassModel predicateClassModel = ClassModel.builder(Predicate.class).enableDiscriminator(true) + .build(); + ClassModel regexPredicateClassModel = ClassModel.builder(RegexPredicate.class) + .enableDiscriminator(true).build(); + ClassModel startsWithPredicateClassModel = ClassModel.builder(StartsWithPredicate.class) + .enableDiscriminator(true).build(); + ClassModel endsWithPredicateClassModel = ClassModel.builder(EndsWithPredicate.class) + .enableDiscriminator(true).build(); + ClassModel equalsToPredicateClassModel = ClassModel.builder(EqualsToPredicate.class) + .enableDiscriminator(true).build(); + ClassModel isNumberPredicateClassModel = ClassModel.builder(IsNumberPredicate.class) + .enableDiscriminator(true).build(); + ClassModel conditionsClassModel = ClassModel.builder(Conditions.class).enableDiscriminator(true) + .build(); + ClassModel cappedListClassModel = ClassModel.builder(CappedList.class).enableDiscriminator(true) + .build(); + ClassModel testingRunClassModel = ClassModel.builder(TestingRun.class).enableDiscriminator(true) + .build(); + ClassModel testingRunResultClassModel = ClassModel.builder(TestingRunResult.class) + .enableDiscriminator(true).build(); + ClassModel testResultClassModel = ClassModel.builder(TestResult.class).enableDiscriminator(true) + .build(); + ClassModel authMechanismClassModel = ClassModel.builder(AuthMechanism.class) + .enableDiscriminator(true).build(); + ClassModel authParamClassModel = ClassModel.builder(AuthParam.class).enableDiscriminator(true) + .build(); + ClassModel hardcodedAuthParamClassModel = ClassModel.builder(HardcodedAuthParam.class) + .enableDiscriminator(true).build(); + ClassModel loginReqAuthParamClassModel = ClassModel.builder(LoginRequestAuthParam.class) + .enableDiscriminator(true).build(); + ClassModel testingEndpointsClassModel = ClassModel.builder(TestingEndpoints.class) + .enableDiscriminator(true).build(); + ClassModel customTestingEndpointsClassModel = ClassModel + .builder(CustomTestingEndpoints.class).enableDiscriminator(true).build(); + ClassModel collectionWiseTestingEndpointsClassModel = ClassModel + .builder(CollectionWiseTestingEndpoints.class).enableDiscriminator(true).build(); + ClassModel workflowTestingEndpointsClassModel = ClassModel + .builder(WorkflowTestingEndpoints.class).enableDiscriminator(true).build(); + ClassModel workflowTestResultClassModel = ClassModel.builder(WorkflowTestResult.class) + .enableDiscriminator(true).build(); + ClassModel cappedSetClassModel = ClassModel.builder(CappedSet.class).enableDiscriminator(true) + .build(); + ClassModel CustomWebhookClassModel = ClassModel.builder(CustomWebhook.class) + .enableDiscriminator(true).build(); + ClassModel CustomWebhookResultClassModel = ClassModel.builder(CustomWebhookResult.class) + .enableDiscriminator(true).build(); + ClassModel nodeResultClassModel = ClassModel + .builder(WorkflowTestResult.NodeResult.class).enableDiscriminator(true).build(); + ClassModel testingRunIssuesClassModel = ClassModel + .builder(TestingRunIssues.class).enableDiscriminator(true).build(); + ClassModel testingIssuesIdClassModel = ClassModel + .builder(TestingIssuesId.class).enableDiscriminator(true).build(); + ClassModel testSourceConfigClassModel = ClassModel + .builder(TestSourceConfig.class).enableDiscriminator(true).build(); + ClassModel endpointLogicalGroupClassModel = ClassModel + .builder(EndpointLogicalGroup.class).enableDiscriminator(true).build(); + ClassModel testRolesClassModel = ClassModel + .builder(TestRoles.class).enableDiscriminator(true).build(); + ClassModel logicalGroupTestingEndpointClassModel = ClassModel + .builder(LogicalGroupTestingEndpoint.class).enableDiscriminator(true).build(); + ClassModel customAuthTypeModel = ClassModel + .builder(CustomAuthType.class).enableDiscriminator(true).build(); + ClassModel containsPredicateClassModel = ClassModel + .builder(ContainsPredicate.class).enableDiscriminator(true).build(); + ClassModel notBelongsToPredicateClassModel = ClassModel + .builder(NotBelongsToPredicate.class).enableDiscriminator(true).build(); + ClassModel belongsToPredicateClassModel = ClassModel + .builder(BelongsToPredicate.class).enableDiscriminator(true).build(); + // ClassModel awsResourceModel = + // ClassModel.builder(AwsResource.class).enableDiscriminator(true) + // .build(); + ClassModel awsResourcesModel = ClassModel.builder(AwsResources.class).enableDiscriminator(true) + .build(); + ClassModel AktoDataTypeClassModel = ClassModel.builder(AktoDataType.class).enableDiscriminator(true).build(); + ClassModel testInfoClassModel = ClassModel.builder(TestInfo.class).enableDiscriminator(true).build(); + ClassModel bflaTestInfoClassModel = ClassModel.builder(BFLATestInfo.class).enableDiscriminator(true).build(); + ClassModel nucleiTestInfoClassModel = ClassModel.builder(NucleiTestInfo.class).enableDiscriminator(true).build(); + + ClassModel loginFlowStepsData = ClassModel.builder(LoginFlowStepsData.class) + .enableDiscriminator(true).build(); + + CodecRegistry pojoCodecRegistry = fromProviders(PojoCodecProvider.builder().register( + configClassModel, + signupInfoClassModel, apiAuthClassModel, attempResultModel, urlTemplateModel, + pendingInviteCodeClassModel, rbacClassModel, kafkaHealthMetricClassModel, singleTypeInfoClassModel, + thirdPartyAccessClassModel, credentialClassModel, apiTokenClassModel, apiInfoClassModel, + apiInfoKeyClassModel, customFilterClassModel, runtimeFilterClassModel, filterSampleDataClassModel, + predicateClassModel, conditionsClassModel, regexPredicateClassModel, startsWithPredicateClassModel, + endsWithPredicateClassModel, + fieldExistsFilterClassModel, accountSettingsClassModel, responseCodeRuntimeFilterClassModel, + cappedListClassModel, + equalsToPredicateClassModel, isNumberPredicateClassModel, testingRunClassModel, + testingRunResultClassModel, testResultClassModel, + authMechanismClassModel, authParamClassModel, hardcodedAuthParamClassModel, loginReqAuthParamClassModel, + testingEndpointsClassModel, customTestingEndpointsClassModel, collectionWiseTestingEndpointsClassModel, + workflowTestingEndpointsClassModel, workflowTestResultClassModel, + cappedSetClassModel, CustomWebhookClassModel, CustomWebhookResultClassModel, + nodeResultClassModel, awsResourcesModel, AktoDataTypeClassModel, testingRunIssuesClassModel, + testingIssuesIdClassModel, testSourceConfigClassModel, endpointLogicalGroupClassModel, testRolesClassModel, + logicalGroupTestingEndpointClassModel, testInfoClassModel , bflaTestInfoClassModel, nucleiTestInfoClassModel, customAuthTypeModel, + containsPredicateClassModel, notBelongsToPredicateClassModel, belongsToPredicateClassModel, loginFlowStepsData).automatic(true).build()); + + final CodecRegistry customEnumCodecs = CodecRegistries.fromCodecs( + new EnumCodec<>(Conditions.Operator.class), + new EnumCodec<>(SingleTypeInfo.SuperType.class), + new EnumCodec<>(Method.class), + new EnumCodec<>(RBAC.Role.class), + new EnumCodec<>(Credential.Type.class), + new EnumCodec<>(ApiToken.Utility.class), + new EnumCodec<>(ApiInfo.AuthType.class), + new EnumCodec<>(ApiInfo.ApiAccessType.class), + new EnumCodec<>(TestResult.TestError.class), + new EnumCodec<>(AuthParam.Location.class), + new EnumCodec<>(TestingEndpoints.Type.class), + new EnumCodec<>(TestingRun.State.class), + new EnumCodec<>(AccountSettings.SetupType.class), + new EnumCodec<>(WorkflowNodeDetails.Type.class), + new EnumCodec<>(SingleTypeInfo.Domain.class), + new EnumCodec<>(CustomWebhook.ActiveStatus.class), + new EnumCodec<>(TestResult.Confidence.class), + new EnumCodec<>(SingleTypeInfo.Position.class), + new EnumCodec<>(TestResult.Confidence.class), + new EnumCodec<>(GlobalEnums.TestRunIssueStatus.class), + new EnumCodec<>(GlobalEnums.TestErrorSource.class), + new EnumCodec<>(GlobalEnums.TestCategory.class), + new EnumCodec<>(GlobalEnums.TestSubCategory.class), + new EnumCodec<>(GlobalEnums.IssueTags.class), + new EnumCodec<>(GlobalEnums.Severity.class)); + + CodecRegistry codecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(), pojoCodecRegistry, + customEnumCodecs); + MongoClientSettings clientSettings = MongoClientSettings.builder() + .applyConnectionString(connectionString) + .codecRegistry(codecRegistry) + .build(); + + clients[0] = MongoClients.create(clientSettings); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/APIAuthDao.java b/libs/dao/src/main/java/com/akto/dao/APIAuthDao.java new file mode 100644 index 0000000000..e02d061782 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/APIAuthDao.java @@ -0,0 +1,21 @@ +package com.akto.dao; + +import com.akto.dto.auth.APIAuth; + +public class APIAuthDao extends AccountsContextDao { + + public static final APIAuthDao instance = new APIAuthDao(); + + private APIAuthDao() {} + + @Override + public String getCollName() { + return "api_auth"; + } + + @Override + public Class getClassT() { + return APIAuth.class; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/APIConfigsDao.java b/libs/dao/src/main/java/com/akto/dao/APIConfigsDao.java new file mode 100644 index 0000000000..9e20c4f5ca --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/APIConfigsDao.java @@ -0,0 +1,17 @@ +package com.akto.dao; + +import com.akto.dto.APIConfig; + +public class APIConfigsDao extends CommonContextDao{ + public static final APIConfigsDao instance = new APIConfigsDao(); + + @Override + public String getCollName() { + return "api_configs"; + } + + @Override + public Class getClassT() { + return APIConfig.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/APISpecDao.java b/libs/dao/src/main/java/com/akto/dao/APISpecDao.java new file mode 100644 index 0000000000..65c8cc1a0d --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/APISpecDao.java @@ -0,0 +1,25 @@ +package com.akto.dao; + +import com.akto.dto.APISpec; + +public class APISpecDao extends AccountsContextDao { + + public static final APISpecDao instance = new APISpecDao(); + + private APISpecDao() {} + + @Override + public String getCollName() { + return "apispec"; + } + + @Override + public Class getClassT() { + return APISpec.class; + } + + public APISpec findById(int id) { + return this.findOne("apiCollectionId", id); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/AccountSettingsDao.java b/libs/dao/src/main/java/com/akto/dao/AccountSettingsDao.java new file mode 100644 index 0000000000..5e7dc3dcf8 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/AccountSettingsDao.java @@ -0,0 +1,52 @@ +package com.akto.dao; + +import com.akto.dao.context.Context; +import com.akto.dto.AccountSettings; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import org.bson.conversions.Bson; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class AccountSettingsDao extends AccountsContextDao { + + public static Bson generateFilter() { + return generateFilter(Context.accountId.get()); + } + + public static Bson generateFilter(int accountId) { + return Filters.eq("_id", accountId); + } + + public static final AccountSettingsDao instance = new AccountSettingsDao(); + + @Override + public String getCollName() { + return "accounts_settings"; + } + + @Override + public Class getClassT() { + return AccountSettings.class; + } + + public void updateVersion(String fieldName) throws Exception { + try (InputStream in = getClass().getResourceAsStream("/version.txt")) { + if (in != null) { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in)); + String imageTag = bufferedReader.readLine(); + String buildTime = bufferedReader.readLine(); + + String version = imageTag + " - " + buildTime; + AccountSettingsDao.instance.updateOne( + AccountSettingsDao.generateFilter(), + Updates.set(fieldName, version) + ); + } else { + throw new Exception("Input stream null"); + } + } + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/AccountsContextDao.java b/libs/dao/src/main/java/com/akto/dao/AccountsContextDao.java new file mode 100644 index 0000000000..6c5e6adfa3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/AccountsContextDao.java @@ -0,0 +1,10 @@ +package com.akto.dao; + +import com.akto.dao.context.Context; + +abstract public class AccountsContextDao extends MCollection { + @Override + public String getDBName() { + return Context.accountId.get()+""; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/AccountsDao.java b/libs/dao/src/main/java/com/akto/dao/AccountsDao.java new file mode 100644 index 0000000000..55bba2668c --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/AccountsDao.java @@ -0,0 +1,25 @@ +package com.akto.dao; + +import com.akto.dto.Account; +import com.mongodb.BasicDBObject; + +import java.util.List; + +public class AccountsDao extends CommonContextDao { + + public static final AccountsDao instance = new AccountsDao(); + + @Override + public String getCollName() { + return "accounts"; + } + + @Override + public Class getClassT() { + return Account.class; + } + + public List getAllAccounts() { + return findAll(new BasicDBObject()); + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/AktoDataTypeDao.java b/libs/dao/src/main/java/com/akto/dao/AktoDataTypeDao.java new file mode 100644 index 0000000000..42563cf4bd --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/AktoDataTypeDao.java @@ -0,0 +1,18 @@ +package com.akto.dao; + +import com.akto.dto.AktoDataType; + +public class AktoDataTypeDao extends AccountsContextDao { + + public static final AktoDataTypeDao instance = new AktoDataTypeDao(); + + @Override + public String getCollName() { + return "akto_data_type"; + } + + @Override + public Class getClassT() { + return AktoDataType.class; + } +} \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dao/ApiCollectionsDao.java b/libs/dao/src/main/java/com/akto/dao/ApiCollectionsDao.java new file mode 100644 index 0000000000..0a66188d1c --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/ApiCollectionsDao.java @@ -0,0 +1,113 @@ +package com.akto.dao; + +import com.akto.dto.ApiCollection; +import com.mongodb.BasicDBObject; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Accumulators; +import com.mongodb.client.model.Aggregates; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Projections; + +import org.bson.conversions.Bson; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ApiCollectionsDao extends AccountsContextDao { + + public static final ApiCollectionsDao instance = new ApiCollectionsDao(); + + private ApiCollectionsDao() {} + + @Override + public String getCollName() { + return "api_collections"; + } + + @Override + public Class getClassT() { + return ApiCollection.class; + } + + public ApiCollection getMeta(int apiCollectionId) { + List ret = ApiCollectionsDao.instance.findAll(Filters.eq("_id", apiCollectionId), Projections.exclude("urls")); + + return (ret != null && ret.size() > 0) ? ret.get(0) : null; + } + + public List getMetaAll() { + return ApiCollectionsDao.instance.findAll(new BasicDBObject(), Projections.exclude("urls")); + } + + public Map generateApiCollectionMap() { + Map apiCollectionMap = new HashMap<>(); + List apiCollections = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + for (ApiCollection apiCollection: apiCollections) { + apiCollectionMap.put(apiCollection.getId(), apiCollection); + } + + return apiCollectionMap; + } + + public ApiCollection findByName(String name) { + List apiCollections = ApiCollectionsDao.instance.findAll(new BasicDBObject()); + for (ApiCollection apiCollection: apiCollections) { + if (apiCollection.getDisplayName() == null) continue; + if (apiCollection.getDisplayName().equalsIgnoreCase(name)) { + return apiCollection; + } + } + return null; + } + + public ApiCollection findByHost(String host) { + return instance.findOne(ApiCollection.HOST_NAME, host); + } + + // this is flawed. Because we were in a hurry we allowed this + // traffic collection with internal api... do not have hosts and will also be included + public List fetchNonTrafficApiCollections() { + return instance.findAll( + Filters.or( + Filters.eq(ApiCollection.HOST_NAME, null), + Filters.exists(ApiCollection.HOST_NAME, false) + ) + ); + } + + public List fetchNonTrafficApiCollectionsIds() { + List nonTrafficApiCollections = ApiCollectionsDao.instance.fetchNonTrafficApiCollections(); + List apiCollectionIds = new ArrayList<>(); + for (ApiCollection apiCollection: nonTrafficApiCollections) { + apiCollectionIds.add(apiCollection.getId()); + } + + return apiCollectionIds; + } + + public Map buildEndpointsCountToApiCollectionMap() { + Map countMap = new HashMap<>(); + List pipeline = new ArrayList<>(); + + pipeline.add(Aggregates.match(SingleTypeInfoDao.filterForHostHeader(0, false))); + + BasicDBObject groupedId = new BasicDBObject("apiCollectionId", "$apiCollectionId"); + pipeline.add(Aggregates.group(groupedId, Accumulators.sum("count",1))); + + MongoCursor endpointsCursor = SingleTypeInfoDao.instance.getMCollection().aggregate(pipeline, BasicDBObject.class).cursor(); + while(endpointsCursor.hasNext()) { + try { + BasicDBObject basicDBObject = endpointsCursor.next(); + int apiCollectionId = ((BasicDBObject) basicDBObject.get("_id")).getInt("apiCollectionId"); + int count = basicDBObject.getInt("count"); + countMap.put(apiCollectionId, count); + } catch (Exception e) { + ; + } + } + + return countMap; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/ApiInfoDao.java b/libs/dao/src/main/java/com/akto/dao/ApiInfoDao.java new file mode 100644 index 0000000000..3cea465127 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/ApiInfoDao.java @@ -0,0 +1,80 @@ +package com.akto.dao; + +import com.akto.dao.context.Context; +import com.akto.dto.ApiInfo; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Indexes; + +import org.bson.Document; +import org.bson.conversions.Bson; + +import java.util.List; + +public class ApiInfoDao extends AccountsContextDao{ + + public static ApiInfoDao instance = new ApiInfoDao(); + + public void createIndicesIfAbsent() { + + boolean exists = false; + for (String col: clients[0].getDatabase(Context.accountId.get()+"").listCollectionNames()){ + if (getCollName().equalsIgnoreCase(col)){ + exists = true; + break; + } + }; + + if (!exists) { + clients[0].getDatabase(Context.accountId.get()+"").createCollection(getCollName()); + } + + MongoCursor cursor = instance.getMCollection().listIndexes().cursor(); + int counter = 0; + while (cursor.hasNext()) { + counter++; + cursor.next(); + } + + if (counter == 1) { + String[] fieldNames = {"_id.apiCollectionId"}; + ApiInfoDao.instance.getMCollection().createIndex(Indexes.ascending(fieldNames)); + counter++; + } + + if (counter == 2) { + String[] fieldNames = {"_id.url"}; + ApiInfoDao.instance.getMCollection().createIndex(Indexes.ascending(fieldNames)); + counter++; + } + + if (counter == 3) { + String[] fieldNames = {"_id.apiCollectionId", "_id.url"}; + ApiInfoDao.instance.getMCollection().createIndex(Indexes.ascending(fieldNames)); + counter++; + } + } + + @Override + public String getCollName() { + return "api_info"; + } + + @Override + public Class getClassT() { + return ApiInfo.class; + } + + public static Bson getFilter(ApiInfo.ApiInfoKey apiInfoKey) { + return getFilter(apiInfoKey.getUrl(), apiInfoKey.getMethod().name(), apiInfoKey.getApiCollectionId()); + } + + public static Bson getFilter(String url, String method, int apiCollectionId) { + return Filters.and( + Filters.eq("_id.url", url), + Filters.eq("_id.method", method), + Filters.eq("_id.apiCollectionId", apiCollectionId) + ); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/ApiTokensDao.java b/libs/dao/src/main/java/com/akto/dao/ApiTokensDao.java new file mode 100644 index 0000000000..569b555673 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/ApiTokensDao.java @@ -0,0 +1,23 @@ +package com.akto.dao; + +import com.akto.dto.ApiToken; +import com.mongodb.client.model.Filters; + +public class ApiTokensDao extends CommonContextDao{ + + public static final ApiTokensDao instance = new ApiTokensDao(); + + @Override + public String getCollName() { + return "api_tokens"; + } + + @Override + public Class getClassT() { + return ApiToken.class; + } + + public ApiToken findByKey(String key) { + return instance.findOne(Filters.eq(ApiToken.KEY, key)); + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/AttemptsDao.java b/libs/dao/src/main/java/com/akto/dao/AttemptsDao.java new file mode 100644 index 0000000000..85f92deb5c --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/AttemptsDao.java @@ -0,0 +1,30 @@ +package com.akto.dao; + +import com.akto.dto.Attempt; + +import org.bson.BsonValue; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; + +public class AttemptsDao extends AccountsContextDao { + + public static final AttemptsDao instance = new AttemptsDao(); + + private AttemptsDao() {} + + @Override + public String getCollName() { + return "attempts"; + } + + @Override + public Class getClassT() { + return Attempt.class; + } + + public void insertAttempts(List attempts) { + insertMany(attempts); + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/AuthMechanismsDao.java b/libs/dao/src/main/java/com/akto/dao/AuthMechanismsDao.java new file mode 100644 index 0000000000..ccd7055f39 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/AuthMechanismsDao.java @@ -0,0 +1,18 @@ +package com.akto.dao; + +import com.akto.dto.testing.AuthMechanism; + +public class AuthMechanismsDao extends AccountsContextDao { + + public static AuthMechanismsDao instance = new AuthMechanismsDao(); + + @Override + public String getCollName() { + return "auth_mechanisms"; + } + + @Override + public Class getClassT() { + return AuthMechanism.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/AwsResourcesDao.java b/libs/dao/src/main/java/com/akto/dao/AwsResourcesDao.java new file mode 100644 index 0000000000..ec5277fd32 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/AwsResourcesDao.java @@ -0,0 +1,30 @@ +package com.akto.dao; + +import org.bson.conversions.Bson; + +import com.akto.dao.context.Context; +import com.akto.dto.AwsResources; +import com.mongodb.client.model.Filters; + +public class AwsResourcesDao extends AccountsContextDao { + + @Override + public String getCollName() { + return "aws_resources"; + } + + @Override + public Class getClassT() { + return AwsResources.class; + } + + public static final AwsResourcesDao instance = new AwsResourcesDao(); + + public static Bson generateFilter() { + return generateFilter(Context.accountId.get()); + } + + public static Bson generateFilter(int accountId) { + return Filters.eq("_id", accountId); + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/BackwardCompatibilityDao.java b/libs/dao/src/main/java/com/akto/dao/BackwardCompatibilityDao.java new file mode 100644 index 0000000000..4ce0e26066 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/BackwardCompatibilityDao.java @@ -0,0 +1,17 @@ +package com.akto.dao; + +import com.akto.dto.BackwardCompatibility; + +public class BackwardCompatibilityDao extends AccountsContextDao{ + + public static final BackwardCompatibilityDao instance = new BackwardCompatibilityDao(); + @Override + public String getCollName() { + return "backward_compatibility"; + } + + @Override + public Class getClassT() { + return BackwardCompatibility.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/BurpPluginInfoDao.java b/libs/dao/src/main/java/com/akto/dao/BurpPluginInfoDao.java new file mode 100644 index 0000000000..a44c6a173a --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/BurpPluginInfoDao.java @@ -0,0 +1,43 @@ +package com.akto.dao; + +import org.bson.conversions.Bson; + +import com.akto.dao.context.Context; +import com.akto.dto.BurpPluginInfo; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; + +public class BurpPluginInfoDao extends AccountsContextDao { + + public static final BurpPluginInfoDao instance = new BurpPluginInfoDao(); + + + public static Bson filterByUsername(String username) { + return Filters.eq(BurpPluginInfo.USERNAME, username); + } + + public void updateLastDownloadedTimestamp(String username) { + instance.updateOne(filterByUsername(username), Updates.set(BurpPluginInfo.LAST_DOWNLOAD_TIMESTAMP, Context.now())); + } + + public void updateLastDataSentTimestamp(String username) { + instance.updateOne(filterByUsername(username), Updates.set(BurpPluginInfo.LAST_DATA_SENT_TIMESTAMP, Context.now())); + } + + + public BurpPluginInfo findByUsername(String username) { + return instance.findOne(filterByUsername(username)); + } + + @Override + public String getCollName() { + return "burp_plugin_info"; + } + + @Override + public Class getClassT() { + return BurpPluginInfo.class; + } + + +} diff --git a/libs/dao/src/main/java/com/akto/dao/CommonContextDao.java b/libs/dao/src/main/java/com/akto/dao/CommonContextDao.java new file mode 100644 index 0000000000..873ba5ab04 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/CommonContextDao.java @@ -0,0 +1,8 @@ +package com.akto.dao; + +public abstract class CommonContextDao extends MCollection { + @Override + public String getDBName() { + return "common"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/ConfigsDao.java b/libs/dao/src/main/java/com/akto/dao/ConfigsDao.java new file mode 100644 index 0000000000..ff3891d715 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/ConfigsDao.java @@ -0,0 +1,18 @@ +package com.akto.dao; + +import com.akto.dto.Config; + +public class ConfigsDao extends CommonContextDao { + + public static final ConfigsDao instance = new ConfigsDao(); + + @Override + public String getCollName() { + return "configs"; + } + + @Override + public Class getClassT() { + return Config.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/CustomAuthTypeDao.java b/libs/dao/src/main/java/com/akto/dao/CustomAuthTypeDao.java new file mode 100644 index 0000000000..db6a5d1392 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/CustomAuthTypeDao.java @@ -0,0 +1,18 @@ +package com.akto.dao; + +import com.akto.dto.CustomAuthType; + +public class CustomAuthTypeDao extends AccountsContextDao{ + + public static final CustomAuthTypeDao instance = new CustomAuthTypeDao(); + + @Override + public String getCollName() { + return "custom_auth_type"; + } + + @Override + public Class getClassT() { + return CustomAuthType.class; + } +} \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dao/CustomDataTypeDao.java b/libs/dao/src/main/java/com/akto/dao/CustomDataTypeDao.java new file mode 100644 index 0000000000..40098f4faf --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/CustomDataTypeDao.java @@ -0,0 +1,18 @@ +package com.akto.dao; + +import com.akto.dto.CustomDataType; + +public class CustomDataTypeDao extends AccountsContextDao{ + + public static final CustomDataTypeDao instance = new CustomDataTypeDao(); + + @Override + public String getCollName() { + return "custom_data_type"; + } + + @Override + public Class getClassT() { + return CustomDataType.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/DibsDao.java b/libs/dao/src/main/java/com/akto/dao/DibsDao.java new file mode 100644 index 0000000000..6be1059c3a --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/DibsDao.java @@ -0,0 +1,22 @@ +package com.akto.dao; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.Dibs; + +public class DibsDao extends AccountsContextDao { + + public static final DibsDao instance = new DibsDao(); + + private DibsDao() {} + + @Override + public String getCollName() { + return "dibs"; + } + + @Override + public Class getClassT() { + return Dibs.class; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/EndpointInfoDao.java b/libs/dao/src/main/java/com/akto/dao/EndpointInfoDao.java new file mode 100644 index 0000000000..90cc0f3eb9 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/EndpointInfoDao.java @@ -0,0 +1,28 @@ +package com.akto.dao; + +import java.util.List; + +import com.akto.dto.type.EndpointInfo; +import com.mongodb.BasicDBObject; + +public class EndpointInfoDao extends AccountsContextDao { + + public static final EndpointInfoDao instance = new EndpointInfoDao(); + + private EndpointInfoDao() {} + + @Override + public String getCollName() { + return "endpoints_info"; + } + + @Override + public Class getClassT() { + return EndpointInfo.class; + } + + public List fetchAllEndpoints() { + return this.findAll(new BasicDBObject()); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/FilterSampleDataDao.java b/libs/dao/src/main/java/com/akto/dao/FilterSampleDataDao.java new file mode 100644 index 0000000000..036970627f --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/FilterSampleDataDao.java @@ -0,0 +1,58 @@ +package com.akto.dao; + +import com.akto.dto.ApiInfo; +import com.akto.dto.FilterSampleData; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Projections; +import org.bson.conversions.Bson; + +import java.util.ArrayList; +import java.util.List; + + +public class FilterSampleDataDao extends AccountsContextDao{ + + public static FilterSampleDataDao instance = new FilterSampleDataDao(); + + public List getApiInfoKeys() { + Bson projection = Projections.fields(Projections.include()); + MongoCursor cursor = instance.getMCollection().find().projection(projection).cursor(); + + ArrayList ret = new ArrayList<>(); + + while(cursor.hasNext()) { + FilterSampleData elem = cursor.next(); + ret.add(elem.getId().getApiInfoKey()); + } + + return ret; + } + + public static Bson getFilter(ApiInfo.ApiInfoKey apiInfoKey, int filterId) { + return Filters.and( + Filters.eq("_id.apiInfoKey.url", apiInfoKey.getUrl()), + Filters.eq("_id.apiInfoKey.method", apiInfoKey.getMethod()+""), + Filters.eq("_id.apiInfoKey.apiCollectionId", apiInfoKey.getApiCollectionId()), + Filters.eq("_id.filterId", filterId) + ); + } + + public static Bson getFilterForApiInfoKey(ApiInfo.ApiInfoKey apiInfoKey) { + return Filters.and( + Filters.eq("_id.apiInfoKey.url", apiInfoKey.getUrl()), + Filters.eq("_id.apiInfoKey.method", apiInfoKey.getMethod()+""), + Filters.eq("_id.apiInfoKey.apiCollectionId", apiInfoKey.getApiCollectionId()) + ); + } + + @Override + public String getCollName() { + return "filter_sample_data"; + } + + @Override + public Class getClassT() { + return FilterSampleData.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/KafkaHealthMetricsDao.java b/libs/dao/src/main/java/com/akto/dao/KafkaHealthMetricsDao.java new file mode 100644 index 0000000000..119cebd3a3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/KafkaHealthMetricsDao.java @@ -0,0 +1,18 @@ +package com.akto.dao; + +import com.akto.dto.KafkaHealthMetric; + +public class KafkaHealthMetricsDao extends CommonContextDao { + + public static final KafkaHealthMetricsDao instance = new KafkaHealthMetricsDao(); + + @Override + public String getCollName() { + return "kafka_health_metrics"; + } + + @Override + public Class getClassT() { + return KafkaHealthMetric.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/LogsDao.java b/libs/dao/src/main/java/com/akto/dao/LogsDao.java new file mode 100644 index 0000000000..7d3be13b22 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/LogsDao.java @@ -0,0 +1,59 @@ +package com.akto.dao; + +import java.util.ArrayList; +import java.util.List; + +import com.akto.dao.context.Context; +import com.akto.dto.Log; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.CreateCollectionOptions; +import com.mongodb.client.model.Indexes; + +import org.bson.Document; + +public class LogsDao extends AccountsContextDao { + + public static final LogsDao instance = new LogsDao(); + public void initIndices() { + boolean exists = false; + String dbName = Context.accountId.get()+""; + MongoDatabase db = clients[0].getDatabase(dbName); + for (String col: db.listCollectionNames()){ + if (getCollName().equalsIgnoreCase(col)){ + exists = true; + break; + } + }; + + if (!exists) { + db.createCollection(getCollName(), new CreateCollectionOptions().capped(true).maxDocuments(100_000).sizeInBytes(100_000_000)); + } + + MongoCursor cursor = db.getCollection(getCollName()).listIndexes().cursor(); + List indices = new ArrayList<>(); + + while (cursor.hasNext()) { + indices.add(cursor.next()); + } + + if (indices.size() == 0) { + instance.getMCollection().createIndex(Indexes.descending(Log.TIMESTAMP)); + } + } + + private LogsDao() { + initIndices(); + } + + @Override + public String getCollName() { + return "logs"; + } + + @Override + public Class getClassT() { + return Log.class; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/MCollection.java b/libs/dao/src/main/java/com/akto/dao/MCollection.java new file mode 100644 index 0000000000..944b654ccf --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/MCollection.java @@ -0,0 +1,183 @@ +package com.akto.dao; + +import com.mongodb.bulk.BulkWriteResult; +import com.mongodb.client.*; +import com.mongodb.client.model.*; +import com.mongodb.client.result.DeleteResult; +import com.mongodb.client.result.InsertManyResult; +import com.mongodb.client.result.InsertOneResult; +import org.bson.Document; +import com.mongodb.client.result.UpdateResult; + +import org.bson.conversions.Bson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +import static com.mongodb.client.model.Filters.*; + +public abstract class MCollection { + private Logger logger = LoggerFactory.getLogger(getClassT()); + public static MongoClient[] clients = new MongoClient[1]; + abstract public String getDBName(); + abstract public String getCollName(); + abstract public Class getClassT(); + + public Document getStats() { + MongoDatabase mongoDatabase = clients[0].getDatabase(getDBName()); + return mongoDatabase.runCommand(new Document("serverStatus",1)); + } + + public MongoCollection getMCollection() { + MongoDatabase mongoDatabase = clients[0].getDatabase(getDBName()); + return mongoDatabase.getCollection(getCollName(), getClassT()); + } + + public List findAll(String key, V value) { + return this.findAll(eq(key, value)); + } + + public List findAll(String key1, V value1, String key2, U value2) { + return this.findAll(and(eq(key1, value1), eq(key2, value2))); + } + + public List findAll(String key, Collection values) { + MongoCursor cursor = this.getMCollection().find(in(key, values)).cursor(); + + ArrayList ret = new ArrayList(); + + while(cursor.hasNext()) { + T elem = cursor.next(); + ret.add(elem); + } + + return ret; + } + public List findAll(Bson q) { + return findAll(q, null); + } + + public List findAll(Bson q, Bson projection) { + return findAll(q, 0, 1_000_000, null, projection); + } + + public List findAll(Bson q, int skip, int limit, Bson sort) { + return findAll(q, skip, limit, sort, null); + } + + public List findAll(Bson q, int skip, int limit, Bson sort, Bson projection) { + + FindIterable commands = this.getMCollection().find(q).skip(skip).limit(limit); + + if (projection != null) { + commands.projection(projection); + } + + if (sort != null) { + commands = commands.sort(sort); + } + + MongoCursor cursor = commands.cursor(); + + ArrayList ret = new ArrayList(); + + while(cursor.hasNext()) { + T elem = cursor.next(); + ret.add(elem); + } + + return ret; + } + + public T findOne(String key, V value) { + return this.findOne(eq(key, value)); + } + + public T findOne(String key1, V value1, String key2, U value2) { + return this.findOne(and(eq(key1, value1), eq(key2, value2))); + } + + public T findOne(String key, Collection values) { + return this.findOne(eq(key, values)); + } + + public T findLatestOne(Bson q) { + MongoCursor cursor = this.getMCollection().find(q).limit(1).sort(Sorts.descending("_id")).cursor(); + + while(cursor.hasNext()) { + T elem = cursor.next(); + return elem; + } + + return null; + } + + public T findOne(Bson q) { + MongoCursor cursor = this.getMCollection().find(q).cursor(); + + while(cursor.hasNext()) { + T elem = cursor.next(); + return elem; + } + + return null; + } + + public T updateOne(String key, V value, Bson obj) { + return this.updateOne(eq(key, value), obj); + } + + public T updateOne(String key1, V value1, String key2, U value2, Bson obj) { + return this.updateOne(and(eq(key1, value1), eq(key2, value2)), obj); + } + + public T updateOne(String key, Collection values, Bson obj) { + return this.updateOne(eq(key, values), obj); + } + + public T updateOne(Bson q, Bson obj) { + return this.getMCollection().findOneAndUpdate(q, obj, new FindOneAndUpdateOptions().upsert(true)); + } + + public UpdateResult updateMany (Bson q, Bson obj) { + return this.getMCollection().updateMany(q, obj); + } + public BulkWriteResult bulkWrite (List> modelList, BulkWriteOptions options) { + return this.getMCollection().bulkWrite(modelList, options); + } + + public UpdateResult replaceOne(Bson q, T obj) { + return this.getMCollection().replaceOne(q, obj, new ReplaceOptions().upsert(true)); + } + + public InsertOneResult insertOne(T elem) { + return getMCollection().insertOne(elem); + } + + public InsertManyResult insertMany(List elems) { + + return getMCollection().insertMany(elems); + } + + + + public DeleteResult deleteAll(Bson q) { + return this.getMCollection().deleteMany(q); + } + + + public Set findDistinctFields(String fieldName, Class resultClass, Bson filter) { + DistinctIterable r = getMCollection().distinct(fieldName,filter,resultClass); + Set result = new HashSet<>(); + MongoCursor cursor = r.cursor(); + while (cursor.hasNext()) { + result.add(cursor.next()); + } + return result; + } + + public Logger getLogger() { + return logger; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/MarkovDao.java b/libs/dao/src/main/java/com/akto/dao/MarkovDao.java new file mode 100644 index 0000000000..6c524b4742 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/MarkovDao.java @@ -0,0 +1,29 @@ +package com.akto.dao; + +import com.akto.dto.Markov; +import com.akto.dto.type.SingleTypeInfo; +import com.mongodb.BasicDBObject; + +import java.util.List; + +public class MarkovDao extends AccountsContextDao { + + public static final MarkovDao instance = new MarkovDao(); + + private MarkovDao() {} + + @Override + public String getCollName() { + return "markov"; + } + + @Override + public Class getClassT() { + return Markov.class; + } + + public List fetchAll() { + return this.findAll(new BasicDBObject()); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/OtpMessagesDao.java b/libs/dao/src/main/java/com/akto/dao/OtpMessagesDao.java new file mode 100644 index 0000000000..dfe4a4adfe --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/OtpMessagesDao.java @@ -0,0 +1,17 @@ +package com.akto.dao; + +import com.akto.dto.OTPMessage; + +public class OtpMessagesDao extends AccountsContextDao{ + + public static final OtpMessagesDao instance = new OtpMessagesDao(); + @Override + public String getCollName() { + return "otp_messages"; + } + + @Override + public Class getClassT() { + return OTPMessage.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/OtpTestDataDao.java b/libs/dao/src/main/java/com/akto/dao/OtpTestDataDao.java new file mode 100644 index 0000000000..ea26f00a65 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/OtpTestDataDao.java @@ -0,0 +1,18 @@ +package com.akto.dao; + +import com.akto.dto.testing.OtpTestData; + +public class OtpTestDataDao extends CommonContextDao { + + public static OtpTestDataDao instance = new OtpTestDataDao(); + + @Override + public String getCollName() { + return "otp_test_data"; + } + + @Override + public Class getClassT() { + return OtpTestData.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/PendingInviteCodesDao.java b/libs/dao/src/main/java/com/akto/dao/PendingInviteCodesDao.java new file mode 100644 index 0000000000..7c2bf5ae72 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/PendingInviteCodesDao.java @@ -0,0 +1,18 @@ +package com.akto.dao; + +import com.akto.dto.PendingInviteCode; + +public class PendingInviteCodesDao extends CommonContextDao{ + + public static PendingInviteCodesDao instance = new PendingInviteCodesDao(); + + @Override + public String getCollName() { + return "invite_codes"; + } + + @Override + public Class getClassT() { + return PendingInviteCode.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/RBACDao.java b/libs/dao/src/main/java/com/akto/dao/RBACDao.java new file mode 100644 index 0000000000..0f68ee38f9 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/RBACDao.java @@ -0,0 +1,29 @@ +package com.akto.dao; + + +import com.akto.dto.RBAC; +import com.mongodb.client.model.Filters; + +public class RBACDao extends CommonContextDao { + public static final RBACDao instance = new RBACDao(); + + public boolean isAdmin(int userId) { + RBAC rbac = RBACDao.instance.findOne( + Filters.and( + Filters.eq(RBAC.USER_ID, userId), + Filters.eq(RBAC.ROLE, RBAC.Role.ADMIN) + ) + ); + return rbac != null; + } + + @Override + public String getCollName() { + return "rbac"; + } + + @Override + public Class getClassT() { + return RBAC.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/RecordedLoginInputDao.java b/libs/dao/src/main/java/com/akto/dao/RecordedLoginInputDao.java new file mode 100644 index 0000000000..3c962db9f1 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/RecordedLoginInputDao.java @@ -0,0 +1,17 @@ +package com.akto.dao; + +import com.akto.dto.RecordedLoginFlowInput; + +public class RecordedLoginInputDao extends AccountsContextDao { + + public static final RecordedLoginInputDao instance = new RecordedLoginInputDao(); + @Override + public String getCollName() { + return "recorded_login_flow_input"; + } + + @Override + public Class getClassT() { + return RecordedLoginFlowInput.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/RelationshipDao.java b/libs/dao/src/main/java/com/akto/dao/RelationshipDao.java new file mode 100644 index 0000000000..c633922476 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/RelationshipDao.java @@ -0,0 +1,29 @@ +package com.akto.dao; + +import com.akto.dto.Markov; +import com.akto.dto.Relationship; +import com.mongodb.BasicDBObject; + +import java.util.List; + +public class RelationshipDao extends AccountsContextDao { + + public static final RelationshipDao instance = new RelationshipDao(); + + private RelationshipDao() {} + + @Override + public String getCollName() { + return "relationship"; + } + + @Override + public Class getClassT() { + return Relationship.class; + } + + public List fetchAll() { + return this.findAll(new BasicDBObject()); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/RequestTemplatesDao.java b/libs/dao/src/main/java/com/akto/dao/RequestTemplatesDao.java new file mode 100644 index 0000000000..820d4ca05a --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/RequestTemplatesDao.java @@ -0,0 +1,28 @@ +package com.akto.dao; + +import com.akto.dto.type.RequestTemplate; +import com.mongodb.BasicDBObject; + +import java.util.List; + +public class RequestTemplatesDao extends AccountsContextDao { + + public static final RequestTemplatesDao instance = new RequestTemplatesDao(); + + private RequestTemplatesDao() {} + + @Override + public String getCollName() { + return "request_templates"; + } + + @Override + public Class getClassT() { + return RequestTemplate.class; + } + + public List fetchAll() { + return this.findAll(new BasicDBObject()); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/RuntimeFilterDao.java b/libs/dao/src/main/java/com/akto/dao/RuntimeFilterDao.java new file mode 100644 index 0000000000..a0c432ad0b --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/RuntimeFilterDao.java @@ -0,0 +1,50 @@ +package com.akto.dao; + +import com.akto.dao.context.Context; +import com.akto.dto.runtime_filters.RuntimeFilter; +import com.mongodb.BasicDBObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.akto.dto.runtime_filters.RuntimeFilter.API_ACCESS_TYPE_FILTER; +import static com.akto.dto.runtime_filters.RuntimeFilter.OPEN_ENDPOINTS_FILTER; + +public class RuntimeFilterDao extends AccountsContextDao{ + public static RuntimeFilterDao instance = new RuntimeFilterDao(); + private static final Logger logger = LoggerFactory.getLogger(RuntimeFilterDao.class); + + + public void initialiseFilters() { + List runtimeFilters = instance.findAll(new BasicDBObject()); + logger.info("FOUND: " + runtimeFilters.size() + " runtime filters"); + Map runtimeFilterMap = new HashMap<>(); + int t = Context.now(); + // to avoid duplicate keys we increment the index by 1 + runtimeFilterMap.put(OPEN_ENDPOINTS_FILTER, RuntimeFilter.generateOpenEndpointsFilter(t)); + runtimeFilterMap.put(API_ACCESS_TYPE_FILTER, RuntimeFilter.generateApiAccessTypeFilter(t+1)); + for (RuntimeFilter runtimeFilter: runtimeFilters) { + runtimeFilterMap.remove(runtimeFilter.getName()); + } + + if (runtimeFilterMap.values().size() > 0) { + logger.info("Inserting "+ runtimeFilterMap.keySet().size()+" filters"); + RuntimeFilterDao.instance.insertMany(new ArrayList<>(runtimeFilterMap.values())); + } + + } + + @Override + public String getCollName() { + return "runtime_filters"; + } + + @Override + public Class getClassT() { + return RuntimeFilter.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/SampleDataDao.java b/libs/dao/src/main/java/com/akto/dao/SampleDataDao.java new file mode 100644 index 0000000000..0d1e6ca4de --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/SampleDataDao.java @@ -0,0 +1,90 @@ +package com.akto.dao; + +import com.akto.dao.context.Context; +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.SampleData; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Indexes; +import com.mongodb.client.model.Sorts; +import org.bson.Document; +import org.bson.conversions.Bson; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class SampleDataDao extends AccountsContextDao { + + public static final SampleDataDao instance = new SampleDataDao(); + + @Override + public String getCollName() { + return "sample_data"; + } + + @Override + public Class getClassT() { + return SampleData.class; + } + + public void createIndicesIfAbsent() { + + boolean exists = false; + for (String col: clients[0].getDatabase(Context.accountId.get()+"").listCollectionNames()){ + if (getCollName().equalsIgnoreCase(col)){ + exists = true; + break; + } + }; + + if (!exists) { + clients[0].getDatabase(Context.accountId.get()+"").createCollection(getCollName()); + } + + MongoCursor cursor = instance.getMCollection().listIndexes().cursor(); + int counter = 0; + while (cursor.hasNext()) { + counter++; + cursor.next(); + } + + if (counter == 1) { + String[] fieldNames = {"_id.apiCollectionId", "_id.url", "_id.method"}; + instance.getMCollection().createIndex(Indexes.ascending(fieldNames)); + counter++; + } + + if (counter == 2) { + instance.getMCollection().createIndex(Indexes.ascending("_id.apiCollectionId")); + } + + } + + public List fetchSampleDataPaginated(int apiCollectionId, String lastFetchedUrl, + String lastFetchedMethod, int limit) { + List filters = new ArrayList<>(); + filters.add(Filters.eq("_id.apiCollectionId", apiCollectionId)); + + + if (lastFetchedUrl != null && lastFetchedMethod != null) { + Bson f1 = Filters.gt("_id.url", lastFetchedUrl); + Bson f2 = Filters.and( + Filters.eq("_id.url", lastFetchedUrl), + Filters.gt("_id.method", lastFetchedMethod) + ); + + filters.add( + Filters.or(f1, f2) + ); + } + + Bson sort = Sorts.ascending("_id.url", "_id.method"); + + + return SampleDataDao.instance.findAll( + Filters.and(filters), 0, limit, sort + ); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/ScansDao.java b/libs/dao/src/main/java/com/akto/dao/ScansDao.java new file mode 100644 index 0000000000..f130e95a30 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/ScansDao.java @@ -0,0 +1,17 @@ +package com.akto.dao; + +import com.akto.dto.Scan; + +public class ScansDao extends AccountsContextDao { + + @Override + public String getCollName() { + return "scans"; + } + + @Override + public Class getClassT() { + return Scan.class; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/SensitiveParamInfoDao.java b/libs/dao/src/main/java/com/akto/dao/SensitiveParamInfoDao.java new file mode 100644 index 0000000000..d0a0bd77b3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/SensitiveParamInfoDao.java @@ -0,0 +1,61 @@ +package com.akto.dao; + +import com.akto.dto.SensitiveParamInfo; +import com.mongodb.client.model.Filters; +import org.bson.conversions.Bson; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class SensitiveParamInfoDao extends AccountsContextDao { + + public static final SensitiveParamInfoDao instance = new SensitiveParamInfoDao(); + + @Override + public String getCollName() { + return "sensitive_param_info"; + } + + @Override + public Class getClassT() { + return SensitiveParamInfo.class; + } + + public static Bson getFilters(SensitiveParamInfo sensitiveParamInfo) { + return getFilters(sensitiveParamInfo.getUrl(),sensitiveParamInfo.getMethod(), + sensitiveParamInfo.getResponseCode(), sensitiveParamInfo.isIsHeader(), + sensitiveParamInfo.getParam(), sensitiveParamInfo.getApiCollectionId()); + } + + public static Bson getFilters(String url, String method, int responseCode, boolean isHeader, String param, int apiCollectionId) { + List defaultFilters = new ArrayList<>(); + defaultFilters.add(Filters.eq("url", url)); + defaultFilters.add(Filters.eq("method", method)); + defaultFilters.add(Filters.eq("isHeader", isHeader)); + defaultFilters.add(Filters.eq("param", param)); + defaultFilters.add(Filters.eq("responseCode", responseCode)); + defaultFilters.add(Filters.eq("apiCollectionId", apiCollectionId)); + + return Filters.and(defaultFilters); + } + + public Set getUniqueEndpoints(int apiCollectionId) { + Bson filter = Filters.eq("apiCollectionId", apiCollectionId); + return instance.findDistinctFields("url", String.class, filter); + } + + public List getUnsavedSensitiveParamInfos() { + return SensitiveParamInfoDao.instance.findAll( + Filters.and( + Filters.or( + Filters.eq(SensitiveParamInfo.SAMPLE_DATA_SAVED,false), + Filters.not(Filters.exists(SensitiveParamInfo.SAMPLE_DATA_SAVED)) + ), + Filters.eq(SensitiveParamInfo.SENSITIVE, true) + ) + ); + } + + +} diff --git a/libs/dao/src/main/java/com/akto/dao/SensitiveSampleDataDao.java b/libs/dao/src/main/java/com/akto/dao/SensitiveSampleDataDao.java new file mode 100644 index 0000000000..40ade92eec --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/SensitiveSampleDataDao.java @@ -0,0 +1,62 @@ +package com.akto.dao; + +import com.akto.dao.context.Context; +import com.akto.dto.SensitiveSampleData; +import com.akto.dto.type.SingleTypeInfo; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Indexes; +import org.bson.Document; +import org.bson.conversions.Bson; + +public class SensitiveSampleDataDao extends AccountsContextDao{ + + public static final SensitiveSampleDataDao instance = new SensitiveSampleDataDao(); + @Override + public String getCollName() { + return "sensitive_sample_data"; + } + + @Override + public Class getClassT() { + return SensitiveSampleData.class; + } + + public static Bson getFilters(SingleTypeInfo singleTypeInfo) { + return Filters.and( + Filters.eq("_id.url", singleTypeInfo.getUrl()), + Filters.eq("_id.method", singleTypeInfo.getMethod()), + Filters.eq("_id.responseCode", singleTypeInfo.getResponseCode()), + Filters.eq("_id.isHeader", singleTypeInfo.getIsHeader()), + Filters.eq("_id.param", singleTypeInfo.getParam()), + Filters.eq("_id.subType", singleTypeInfo.getSubType().getName()), + Filters.eq("_id.apiCollectionId", singleTypeInfo.getApiCollectionId()) + ); + } + + public void createIndicesIfAbsent() { + boolean exists = false; + for (String col: clients[0].getDatabase(Context.accountId.get()+"").listCollectionNames()){ + if (getCollName().equalsIgnoreCase(col)){ + exists = true; + break; + } + }; + + if (!exists) { + clients[0].getDatabase(Context.accountId.get()+"").createCollection(getCollName()); + } + + MongoCursor cursor = instance.getMCollection().listIndexes().cursor(); + int counter = 0; + while (cursor.hasNext()) { + counter++; + cursor.next(); + } + + if (counter == 1) { + String[] fieldNames = {"_id.url", "_id.apiCollectionId", "_id.method"}; + instance.getMCollection().createIndex(Indexes.ascending(fieldNames)); + } + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/SignupDao.java b/libs/dao/src/main/java/com/akto/dao/SignupDao.java new file mode 100644 index 0000000000..58268da000 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/SignupDao.java @@ -0,0 +1,43 @@ +package com.akto.dao; + +import com.akto.dto.SignupInfo; +import com.akto.dto.SignupUserInfo; +import com.akto.dto.User; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import static com.mongodb.client.model.Filters.eq; +import static com.mongodb.client.model.Updates.set; + +public class SignupDao extends CommonContextDao { + + public static final SignupDao instance = new SignupDao(); + + @Override + public String getCollName() { + return "signup_info"; + } + + @Override + public Class getClassT() { + return SignupUserInfo.class; + } + + public SignupUserInfo insertSignUp(String email, String name, SignupInfo info) { + SignupUserInfo user = findOne("user.login", email); + if (user == null) { + insertOne(new SignupUserInfo(User.create(name, email, info, new HashMap<>()), null, null, null, new ArrayList<>())); + } else { + Map infoMap = user.getUser().getSignupInfoMap(); + if (infoMap == null) { + infoMap = new HashMap<>(); + } + infoMap.put(info.getKey(), info); + this.getMCollection().updateOne(eq("user.login", email), set("user.signupInfoMap", infoMap)); + } + + return findOne("user.login", email); + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/SingleTypeInfoDao.java b/libs/dao/src/main/java/com/akto/dao/SingleTypeInfoDao.java new file mode 100644 index 0000000000..326eb93b7a --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/SingleTypeInfoDao.java @@ -0,0 +1,317 @@ +package com.akto.dao; + +import java.util.*; + +import com.akto.DaoInit; +import com.akto.dao.context.Context; +import com.akto.dto.ApiInfo; +import com.akto.dto.CustomDataType; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.SensitiveParamInfo; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.*; + +import org.bson.Document; +import org.bson.conversions.Bson; + +public class SingleTypeInfoDao extends AccountsContextDao { + + public static final SingleTypeInfoDao instance = new SingleTypeInfoDao(); + + private SingleTypeInfoDao() {} + + @Override + public String getCollName() { + return "single_type_info"; + } + + @Override + public Class getClassT() { + return SingleTypeInfo.class; + } + + public void createIndicesIfAbsent() { + + boolean exists = false; + for (String col: clients[0].getDatabase(Context.accountId.get()+"").listCollectionNames()){ + if (getCollName().equalsIgnoreCase(col)){ + exists = true; + break; + } + }; + + if (!exists) { + clients[0].getDatabase(Context.accountId.get()+"").createCollection(getCollName()); + } + + MongoCursor cursor = instance.getMCollection().listIndexes().cursor(); + int counter = 0; + while (cursor.hasNext()) { + counter++; + cursor.next(); + } + + if (counter == 1) { + String[] fieldNames = {"url", "method", "responseCode", "isHeader", "param", "subType", "apiCollectionId"}; + SingleTypeInfoDao.instance.getMCollection().createIndex(Indexes.ascending(fieldNames)); + } + + if (counter == 2) { + SingleTypeInfoDao.instance.getMCollection().createIndex(Indexes.ascending(new String[]{"apiCollectionId"})); + counter++; + } + + if (counter == 3) { + SingleTypeInfoDao.instance.getMCollection().createIndex(Indexes.ascending(new String[]{"param", "apiCollectionId"})); + counter++; + } + + if (counter == 4) { + SingleTypeInfoDao.instance.getMCollection().createIndex(Indexes.ascending(new String[]{SingleTypeInfo._RESPONSE_CODE, SingleTypeInfo._IS_HEADER, SingleTypeInfo._PARAM, SingleTypeInfo.SUB_TYPE, SingleTypeInfo._API_COLLECTION_ID})); + counter++; + } + } + + + public static Bson filterForHostHeader(int apiCollectionId, boolean useApiCollectionId) { + List filters = new ArrayList<>(); + filters.add(Filters.eq(SingleTypeInfo._RESPONSE_CODE, -1)); + filters.add(Filters.eq(SingleTypeInfo._IS_HEADER, true)); + filters.add(Filters.eq(SingleTypeInfo._PARAM, "host")); + filters.add(Filters.eq(SingleTypeInfo.SUB_TYPE, SingleTypeInfo.GENERIC.getName())); + + if (useApiCollectionId) filters.add(Filters.eq(SingleTypeInfo._API_COLLECTION_ID, apiCollectionId)); + + return Filters.and(filters); + } + + public List fetchAll() { + return this.findAll(new BasicDBObject()); + } + + public static Bson createFiltersWithoutSubType(SingleTypeInfo info) { + List filters = createFiltersBasic(info); + return Filters.and(filters); + } + + + + public static List createFiltersBasic(SingleTypeInfo info) { + List filters = new ArrayList<>(); + filters.add(Filters.eq("url", info.getUrl())); + filters.add(Filters.eq("method", info.getMethod())); + filters.add(Filters.eq("responseCode", info.getResponseCode())); + filters.add(Filters.eq("isHeader", info.getIsHeader())); + filters.add(Filters.eq("param", info.getParam())); + filters.add(Filters.eq("apiCollectionId", info.getApiCollectionId())); + + List urlParamQuery; + if (info.getIsUrlParam()) { + urlParamQuery = Collections.singletonList(true); + } else { + urlParamQuery = Arrays.asList(false, null); + } + + filters.add(Filters.in("isUrlParam", urlParamQuery)); + return filters; + } + + public static Bson createFilters(SingleTypeInfo info) { + List filters = createFiltersBasic(info); + filters.add(Filters.eq("subType", info.getSubType().getName())); + return Filters.and(filters); + } + + public Set getUniqueEndpoints(int apiCollectionId) { + Bson filter = Filters.eq("apiCollectionId", apiCollectionId); + return instance.findDistinctFields("url", String.class, filter); + } + + public List sensitiveSubTypeNames() { + List sensitiveSubTypes = new ArrayList<>(); + // AKTO sensitive + for (SingleTypeInfo.SubType subType: SingleTypeInfo.subTypeMap.values()) { + if (subType.isSensitiveAlways()) { + sensitiveSubTypes.add(subType.getName()); + } + } + + // Custom data type sensitive + for (CustomDataType customDataType: SingleTypeInfo.customDataTypeMap.values()) { + if (customDataType.isSensitiveAlways()) { + sensitiveSubTypes.add(customDataType.getName()); + } + } + + return sensitiveSubTypes; + } + + public List sensitiveSubTypeInRequestNames() { + List sensitiveInRequest = new ArrayList<>(); + for (SingleTypeInfo.SubType subType: SingleTypeInfo.subTypeMap.values()) { + if (subType.getSensitivePosition().contains(SingleTypeInfo.Position.REQUEST_HEADER) || subType.getSensitivePosition().contains(SingleTypeInfo.Position.REQUEST_PAYLOAD)) { + sensitiveInRequest.add(subType.getName()); + } + } + + for (CustomDataType customDataType: SingleTypeInfo.customDataTypeMap.values()) { + if (customDataType.getSensitivePosition().contains(SingleTypeInfo.Position.REQUEST_HEADER) || customDataType.getSensitivePosition().contains(SingleTypeInfo.Position.REQUEST_PAYLOAD)) { + sensitiveInRequest.add(customDataType.getName()); + } + } + return sensitiveInRequest; + } + + public List sensitiveSubTypeInResponseNames() { + List sensitiveInResponse = new ArrayList<>(); + for (SingleTypeInfo.SubType subType: SingleTypeInfo.subTypeMap.values()) { + if (subType.getSensitivePosition().contains(SingleTypeInfo.Position.RESPONSE_HEADER) || subType.getSensitivePosition().contains(SingleTypeInfo.Position.RESPONSE_PAYLOAD)) { + sensitiveInResponse.add(subType.getName()); + } + } + for (CustomDataType customDataType: SingleTypeInfo.customDataTypeMap.values()) { + if (customDataType.getSensitivePosition().contains(SingleTypeInfo.Position.RESPONSE_HEADER) || customDataType.getSensitivePosition().contains(SingleTypeInfo.Position.RESPONSE_PAYLOAD)) { + sensitiveInResponse.add(customDataType.getName()); + } + } + return sensitiveInResponse; + } + + public Bson filterForSensitiveParamsExcludingUserMarkedSensitive(Integer apiCollectionId, String url, String method) { + // apiCollectionId null then no filter for apiCollectionId + List sensitiveSubTypes = sensitiveSubTypeNames(); + + Bson alwaysSensitiveFilter = Filters.in("subType", sensitiveSubTypes); + + List sensitiveInResponse = sensitiveSubTypeInResponseNames(); + List sensitiveInRequest = sensitiveSubTypeInRequestNames(); + + Bson sensitiveInResponseFilter = Filters.and( + Filters.in("subType",sensitiveInResponse ), + Filters.gt("responseCode", -1) + ); + Bson sensitiveInRequestFilter = Filters.and( + Filters.in("subType",sensitiveInRequest ), + Filters.eq("responseCode", -1) + ); + + List filters = new ArrayList<>(); + filters.add(Filters.or(alwaysSensitiveFilter, sensitiveInResponseFilter, sensitiveInRequestFilter)); + + if (apiCollectionId != null && apiCollectionId >= 0) { + filters.add(Filters.eq("apiCollectionId", apiCollectionId) ); + } + + if (url != null) { + filters.add(Filters.eq("url", url)); + } + + if (method != null) { + filters.add(Filters.eq("method",method)); + } + + return Filters.and(filters); + } + + public Bson filterForAllNewParams(int startTimestamp,int endTimestamp){ + + List filters = new ArrayList<>(); + + filters.add(Filters.gte("timestamp",startTimestamp)); + filters.add(Filters.lte("timestamp",endTimestamp)); + + return Filters.and(filters); + } + + public Set getSensitiveEndpoints(int apiCollectionId, String url, String method) { + Set urls = new HashSet<>(); + + // User manually set sensitive + List customSensitiveList = SensitiveParamInfoDao.instance.findAll( + Filters.and( + Filters.eq("sensitive", true), + Filters.eq("apiCollectionId", apiCollectionId) + ) + ); + for (SensitiveParamInfo sensitiveParamInfo: customSensitiveList) { + urls.add(sensitiveParamInfo.getUrl()); + } + + Bson filter = filterForSensitiveParamsExcludingUserMarkedSensitive(apiCollectionId, url, method); + + urls.addAll(instance.findDistinctFields("url", String.class, filter)); + + return urls; + } + + public void resetCount() { + instance.getMCollection().updateMany( + Filters.gt("count", 0), + Updates.set("count", 0) + ); + } + + + // to get results irrespective of collections use negative value for apiCollectionId + public List fetchEndpointsInCollection(int apiCollectionId) { + List pipeline = new ArrayList<>(); + BasicDBObject groupedId = + new BasicDBObject("apiCollectionId", "$apiCollectionId") + .append("url", "$url") + .append("method", "$method"); + + if (apiCollectionId != -1) { + pipeline.add(Aggregates.match(Filters.eq("apiCollectionId", apiCollectionId))); + } + + Bson projections = Projections.fields( + Projections.include("timestamp", "apiCollectionId", "url", "method") + ); + + pipeline.add(Aggregates.project(projections)); + pipeline.add(Aggregates.group(groupedId)); + pipeline.add(Aggregates.sort(Sorts.descending("startTs"))); + + MongoCursor endpointsCursor = instance.getMCollection().aggregate(pipeline, BasicDBObject.class).cursor(); + + List endpoints = new ArrayList<>(); + while(endpointsCursor.hasNext()) { + BasicDBObject v = endpointsCursor.next(); + try { + BasicDBObject vv = (BasicDBObject) v.get("_id"); + ApiInfo.ApiInfoKey apiInfoKey = new ApiInfo.ApiInfoKey( + (int) vv.get("apiCollectionId"), + (String) vv.get("url"), + URLMethods.Method.fromString((String) vv.get("method")) + ); + endpoints.add(apiInfoKey); + } catch (Exception e) { + ; + + } + } + + return endpoints; + } + + public List fetchStiOfCollections(List apiCollectionIds) { + Bson filters = Filters.in(SingleTypeInfo._API_COLLECTION_ID, apiCollectionIds); + return instance.findAll(filters); + } + + public void deleteValues() { + instance.getMCollection().updateMany( + Filters.exists(SingleTypeInfo._VALUES), + Updates.unset(SingleTypeInfo._VALUES) + ); + } + + public long getEstimatedCount(){ + return instance.getMCollection().estimatedDocumentCount(); + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/TagConfigsDao.java b/libs/dao/src/main/java/com/akto/dao/TagConfigsDao.java new file mode 100644 index 0000000000..399552ab82 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/TagConfigsDao.java @@ -0,0 +1,17 @@ +package com.akto.dao; + +import com.akto.dto.TagConfig; + +public class TagConfigsDao extends AccountsContextDao { + public static final TagConfigsDao instance = new TagConfigsDao(); + + @Override + public String getCollName() { + return "tag_config"; + } + + @Override + public Class getClassT() { + return TagConfig.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/TeamsDao.java b/libs/dao/src/main/java/com/akto/dao/TeamsDao.java new file mode 100644 index 0000000000..f1eba5fdc5 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/TeamsDao.java @@ -0,0 +1,20 @@ +package com.akto.dao; + +import com.akto.dto.Team; + +public class TeamsDao extends AccountsContextDao { + + public static final TeamsDao instance = new TeamsDao(); + + private TeamsDao() {} + + @Override + public String getCollName() { + return "teams"; + } + + @Override + public Class getClassT() { + return Team.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/TestEnvSettingsDao.java b/libs/dao/src/main/java/com/akto/dao/TestEnvSettingsDao.java new file mode 100644 index 0000000000..4e4b6876b0 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/TestEnvSettingsDao.java @@ -0,0 +1,25 @@ +package com.akto.dao; + +import com.akto.dto.TestEnvSettings; + +public class TestEnvSettingsDao extends AccountsContextDao { + + public static final TestEnvSettingsDao instance = new TestEnvSettingsDao(); + + private TestEnvSettingsDao() {} + + @Override + public String getCollName() { + return "test_env_settings"; + } + + @Override + public Class getClassT() { + return TestEnvSettings.class; + } + + public TestEnvSettings get(int id) { + return findOne("_id", id); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/TestRunDao.java b/libs/dao/src/main/java/com/akto/dao/TestRunDao.java new file mode 100644 index 0000000000..95c105f06e --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/TestRunDao.java @@ -0,0 +1,24 @@ +package com.akto.dao; + +import com.akto.dto.TestRun; + +public class TestRunDao extends AccountsContextDao { + + public static final TestRunDao instance = new TestRunDao(); + + private TestRunDao() {} + + @Override + public String getCollName() { + return "test_run"; + } + + @Override + public Class getClassT() { + return TestRun.class; + } + + public int createRun(TestRun testRun) { + return insertOne(testRun).getInsertedId().asInt32().getValue(); + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/ThirdPartyAccessDao.java b/libs/dao/src/main/java/com/akto/dao/ThirdPartyAccessDao.java new file mode 100644 index 0000000000..f18cd74582 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/ThirdPartyAccessDao.java @@ -0,0 +1,18 @@ +package com.akto.dao; + +import com.akto.dto.third_party_access.ThirdPartyAccess; + +public class ThirdPartyAccessDao extends AccountsContextDao { + + public static ThirdPartyAccessDao instance = new ThirdPartyAccessDao(); + + @Override + public String getCollName() { + return "third_party"; + } + + @Override + public Class getClassT() { + return ThirdPartyAccess.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/TrafficInfoDao.java b/libs/dao/src/main/java/com/akto/dao/TrafficInfoDao.java new file mode 100644 index 0000000000..6782489098 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/TrafficInfoDao.java @@ -0,0 +1,18 @@ +package com.akto.dao; + +import com.akto.dto.traffic.TrafficInfo; + +public class TrafficInfoDao extends AccountsContextDao { + + public static final TrafficInfoDao instance = new TrafficInfoDao(); + + @Override + public String getCollName() { + return "traffic_info"; + } + + @Override + public Class getClassT() { + return TrafficInfo.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/URLTemplateDao.java b/libs/dao/src/main/java/com/akto/dao/URLTemplateDao.java new file mode 100644 index 0000000000..fada78de29 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/URLTemplateDao.java @@ -0,0 +1,17 @@ +package com.akto.dao; + +import com.akto.dto.type.URLTemplate; + +public class URLTemplateDao extends AccountsContextDao { + + @Override + public String getCollName() { + return "url_templates"; + } + + @Override + public Class getClassT() { + return URLTemplate.class; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/UsersDao.java b/libs/dao/src/main/java/com/akto/dao/UsersDao.java new file mode 100644 index 0000000000..960d6a1eb7 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/UsersDao.java @@ -0,0 +1,178 @@ +package com.akto.dao; + +import com.akto.dto.RBAC; +import com.akto.dto.SignupInfo; +import com.akto.dto.UserAccountEntry; +import com.akto.dto.User; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Sorts; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import static com.mongodb.client.model.Filters.eq; +import static com.mongodb.client.model.Filters.in; +import static com.mongodb.client.model.Projections.include; +import static com.mongodb.client.model.Updates.combine; +import static com.mongodb.client.model.Updates.set; + +public class UsersDao extends CommonContextDao { + + public static User addUser(String login, String name, String password, boolean emailValidated) { + // Checking if the user with same login exists or not + if (UsersDao.instance.getMCollection().find(eq("login",login)).first() != null) { + return null; + } + String salt = "39yu"; + String passHash = Integer.toString((salt + password).hashCode()); + return null; + } + + public static void addAccount(String login, int accountId) { + BasicDBObject setQ = new BasicDBObject("accounts", new BasicDBObject(""+accountId, new UserAccountEntry(accountId))); + UsersDao.instance.getMCollection().updateOne(eq("login", login), new BasicDBObject("$set", setQ)); + } + + public User insertSignUp(String email, String name, SignupInfo info, int accountId) { + User user = findOne("login", email); + User ret = null; + UserAccountEntry userAccountEntry = new UserAccountEntry(); + userAccountEntry.setAccountId(accountId); + userAccountEntry.setDefault(true); + Map accountAccessMap = new HashMap<>(); + accountAccessMap.put(accountId+"", userAccountEntry); + + if (user == null) { + ret = User.create(name, email, info, accountAccessMap); + insertOne(ret); + } else { + Map infoMap = user.getSignupInfoMap(); + if (infoMap == null) { + infoMap = new HashMap<>(); + } + infoMap.put(info.getKey(), info); + + Map userAccountEntryMap = user.getAccounts(); + if (userAccountEntryMap == null) { + userAccountEntryMap = new HashMap<>(); + } + + userAccountEntryMap.putAll(accountAccessMap); + + this.getMCollection().updateOne(eq("login", email), combine(set("signupInfoMap", infoMap), set("accounts", accountAccessMap))); + } + + return findOne("login", email); + } + + public void insertPushSubscription(String login, BasicDBObject subscription) { + updateOne(eq("login", login), set("signupInfoMap.WEBPUSH-ankush", new SignupInfo.WebpushSubscriptionInfo(subscription))); + } + + + public static User validateEmail(String email) { + return UsersDao.instance.getMCollection().findOneAndUpdate( + Filters.eq("login",email), + set("emailValidated",true) + ); + } + + final public static UsersDao instance = new UsersDao(); + + public User getFirstUser() { + MongoCursor cursor = instance.getMCollection().find().sort(Sorts.ascending("_id")).limit(1).cursor(); + if (cursor.hasNext()) { + return cursor.next(); + } + + return null; + } + + public Map getUsernames(Collection userIds) { + MongoCursor cursor = instance.getMCollection().find(in("_id", userIds)).projection(new BasicDBObject("name", 1)).cursor(); + + Map ret = new HashMap(); + + while (cursor.hasNext()) { + User user = cursor.next(); + ret.put(user.getId(), user.getName()); + } + return ret; + } + + + public BasicDBObject getUserInfo(int user_id) { + User user = instance.getMCollection().find(eq("_id",user_id)).projection( + new BasicDBObject("_id",1) + .append("login",1) + .append("name",1) + ).first(); + + if (user == null) { + return null; + } + return User.convertUserToUserDetails(user); + } + + public Map getUsersInfo(List userIds) { + + MongoCursor cursor = UsersDao.instance.getMCollection().find(in("_id", userIds)).projection(include("login", "name", "_id")).cursor(); + + Map ret = new HashMap<>(); + + while (cursor.hasNext()) { + User user = cursor.next(); + ret.put(user.getId(), user); + } + + return ret; + } + + public BasicDBList getAllUsersInfoForTheAccount(int accountId) { + List users = instance.findAll(Filters.exists("accounts."+accountId)); + BasicDBList result = new BasicDBList(); + + for (User user: users) { + result.add(User.convertUserToUserDetails(user)); + } + + return result; + } + + public BasicDBList getUsersAutoComplete(int accountId, String expression) { + List users = instance.findAll( + Filters.and( + Filters.exists("accounts."+accountId), + Filters.regex("login", Pattern.compile(expression, Pattern.CASE_INSENSITIVE)) + ) + ); + BasicDBList result = new BasicDBList(); + for (User user: users) { + result.add(User.convertUserToUserDetails(user)); + } + return result; + } + + public Integer fetchUserLasLoginTs(int userId) { + User user = instance.getMCollection().find(eq("_id", userId)).projection(new BasicDBObject(User.LAST_LOGIN_TS,1)).first(); + if (user == null) return null; + + return user.getLastLoginTs(); + } + + @Override + public String getCollName() { + return "users"; + } + + @Override + public Class getClassT() { + return User.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/context/Context.java b/libs/dao/src/main/java/com/akto/dao/context/Context.java new file mode 100644 index 0000000000..0f35f1912e --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/context/Context.java @@ -0,0 +1,78 @@ +package com.akto.dao.context; + +import com.akto.dao.AccountsDao; +import com.akto.dto.Account; + +import java.math.BigDecimal; +import java.time.*; +import java.time.format.DateTimeFormatter; + +public class Context { + public static ThreadLocal accountId = new ThreadLocal(); + public static int getId() { + return (int) (System.currentTimeMillis()/1000l); + } + + public static void dummy() { + + } + + public static int today() { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd"); + LocalDateTime now = LocalDateTime.now(); + return Integer.parseInt(dtf.format(now)); + } + + public static Account getAccount() { + return AccountsDao.instance.findOne("_id", Context.accountId.get()); + } + + public static int convertEpochToDateInt(long epoch, String accountTz) { + ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(epoch), ZoneId.of(accountTz)); + return Integer.parseInt(zonedDateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"))); + } + + public static long convertDateIntToEpoch(int dateInt, String accountTz) { + LocalDate localDate = LocalDate.parse( + Integer.toString(dateInt),DateTimeFormatter.ofPattern("yyyyMMdd") + ); + LocalTime localTime = LocalTime.MIDNIGHT; + LocalDateTime localDateTime = LocalDateTime.of(localDate,localTime); + ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime,ZoneId.of(accountTz)); + return zonedDateTime.toInstant().getEpochSecond(); + } + + public static ZonedDateTime convertEpochToZonedDateTime(long epoch, String accountTz) { + return Instant.ofEpochSecond(epoch).atZone(ZoneId.of(accountTz)); + } + + public static ZonedDateTime setDateTimeToFirstOfMonth(ZonedDateTime zonedDateTime) { + zonedDateTime = zonedDateTime.with(LocalTime.MIDNIGHT); + zonedDateTime = zonedDateTime.withDayOfMonth(1); + return zonedDateTime; + } + + public static ZonedDateTime setTimeToMidnight(ZonedDateTime zonedDateTime) { + return zonedDateTime.with(LocalTime.MIDNIGHT); + } + + public static ZonedDateTime setMinutesAndSecondsToZero(ZonedDateTime zonedDateTime) { + return zonedDateTime.withSecond(0).withMinute(0); + } + + public static int now() { + return (int) (System.currentTimeMillis()/1000l); + } + + + public static long dateFromLotusNotation(BigDecimal serial_number, String sourceTz) { + long numSecondsFromSheetEpoch = (long) (serial_number.doubleValue()*24*60*60); + + // Google sheets stores datetime in days from Dec 30 1899 + LocalDateTime start = LocalDateTime.of(1899, 12, 30, 0, 0, 0); + LocalDateTime end = start.plusSeconds(numSecondsFromSheetEpoch); + ZonedDateTime zonedDateTime = end.atZone(ZoneId.of(sourceTz)); + return zonedDateTime.toInstant().getEpochSecond(); + + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/notifications/CustomWebhooksDao.java b/libs/dao/src/main/java/com/akto/dao/notifications/CustomWebhooksDao.java new file mode 100644 index 0000000000..41a3788f6a --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/notifications/CustomWebhooksDao.java @@ -0,0 +1,22 @@ +package com.akto.dao.notifications; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.notifications.CustomWebhook; + +public class CustomWebhooksDao extends AccountsContextDao{ + + public static final CustomWebhooksDao instance = new CustomWebhooksDao(); + + private CustomWebhooksDao() {} + + @Override + public String getCollName() { + return "custom_webhooks"; + } + + @Override + public Class getClassT() { + return CustomWebhook.class; + } + +} \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dao/notifications/CustomWebhooksResultDao.java b/libs/dao/src/main/java/com/akto/dao/notifications/CustomWebhooksResultDao.java new file mode 100644 index 0000000000..95e903b4cb --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/notifications/CustomWebhooksResultDao.java @@ -0,0 +1,22 @@ +package com.akto.dao.notifications; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.notifications.CustomWebhookResult; + +public class CustomWebhooksResultDao extends AccountsContextDao{ + + public static final CustomWebhooksResultDao instance = new CustomWebhooksResultDao(); + + private CustomWebhooksResultDao() {} + + @Override + public String getCollName() { + return "custom_webhooks_result"; + } + + @Override + public Class getClassT() { + return CustomWebhookResult.class; + } + +} \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dao/notifications/SlackWebhooksDao.java b/libs/dao/src/main/java/com/akto/dao/notifications/SlackWebhooksDao.java new file mode 100644 index 0000000000..73ff50d5dc --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/notifications/SlackWebhooksDao.java @@ -0,0 +1,22 @@ +package com.akto.dao.notifications; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.notifications.SlackWebhook; + +public class SlackWebhooksDao extends AccountsContextDao { + + public static final SlackWebhooksDao instance = new SlackWebhooksDao(); + + private SlackWebhooksDao() {} + + @Override + public String getCollName() { + return "slack_webhooks"; + } + + @Override + public Class getClassT() { + return SlackWebhook.class; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/pii/PIISourceDao.java b/libs/dao/src/main/java/com/akto/dao/pii/PIISourceDao.java new file mode 100644 index 0000000000..2cbb804ae1 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/pii/PIISourceDao.java @@ -0,0 +1,21 @@ +package com.akto.dao.pii; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.pii.PIISource; + +public class PIISourceDao extends AccountsContextDao { + + public static final PIISourceDao instance = new PIISourceDao(); + + private PIISourceDao() {} + + @Override + public String getCollName() { + return "pii_sources"; + } + + @Override + public Class getClassT() { + return PIISource.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/EndpointLogicalGroupDao.java b/libs/dao/src/main/java/com/akto/dao/testing/EndpointLogicalGroupDao.java new file mode 100644 index 0000000000..bfe2973363 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/EndpointLogicalGroupDao.java @@ -0,0 +1,50 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDao; +import com.akto.dao.context.Context; +import com.akto.dto.data_types.Conditions; +import com.akto.dto.testing.EndpointLogicalGroup; +import com.akto.dto.testing.LogicalGroupTestingEndpoint; +import com.akto.util.Constants; +import com.akto.util.enums.MongoDBEnums; +import com.mongodb.MongoException; +import com.mongodb.client.model.Filters; +import com.mongodb.client.result.UpdateResult; +import org.bson.types.ObjectId; + +public class EndpointLogicalGroupDao extends AccountsContextDao { + @Override + public String getCollName() { + return MongoDBEnums.Collection.ENDPOINT_LOGICAL_GROUP.getCollectionName(); + } + + @Override + public Class getClassT() { + return EndpointLogicalGroup.class; + } + + public static final EndpointLogicalGroupDao instance = new EndpointLogicalGroupDao(); + + public EndpointLogicalGroup createLogicalGroup(String name, Conditions andConditions, Conditions orConditions, String user) { + LogicalGroupTestingEndpoint testingEndpoint = new LogicalGroupTestingEndpoint(andConditions, orConditions); + int createdTs = Context.now(); + EndpointLogicalGroup endpointLogicalGroup = new EndpointLogicalGroup(new ObjectId(), createdTs, createdTs, user,name, testingEndpoint); + try { + this.insertOne(endpointLogicalGroup); + return endpointLogicalGroup; + } catch (MongoException e) { + getLogger().info("Error while inserting endpoint logical group for name :{}", name); + return null; + } + } + + public UpdateResult updateLogicalGroup (EndpointLogicalGroup group, Conditions andConditions, Conditions orConditions) { + int updateTs = Context.now(); + group.setTestingEndpoints(new LogicalGroupTestingEndpoint(andConditions,orConditions)); + group.setUpdatedTs(updateTs); + return this.replaceOne(Filters.eq(Constants.ID, group.getId()), group); + } + + private EndpointLogicalGroupDao() { + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/LoginFlowStepsDao.java b/libs/dao/src/main/java/com/akto/dao/testing/LoginFlowStepsDao.java new file mode 100644 index 0000000000..8fd9b5276a --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/LoginFlowStepsDao.java @@ -0,0 +1,19 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.testing.LoginFlowStepsData; + +public class LoginFlowStepsDao extends AccountsContextDao { + + public static final LoginFlowStepsDao instance = new LoginFlowStepsDao(); + + @Override + public String getCollName() { + return "login_flow_steps"; + } + + @Override + public Class getClassT() { + return LoginFlowStepsData.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestRolesDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestRolesDao.java new file mode 100644 index 0000000000..934d80b14b --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestRolesDao.java @@ -0,0 +1,59 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDao; +import com.akto.dao.context.Context; +import com.akto.dto.testing.TestRoles; +import com.akto.util.enums.MongoDBEnums; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.IndexOptions; +import com.mongodb.client.model.Indexes; +import org.bson.Document; +import org.bson.types.ObjectId; + +public class TestRolesDao extends AccountsContextDao { + @Override + public String getCollName() { + return MongoDBEnums.Collection.TEST_ROLES.getCollectionName(); + } + public static final TestRolesDao instance = new TestRolesDao(); + private TestRolesDao(){} + @Override + public Class getClassT() { + return TestRoles.class; + } + public void createIndicesIfAbsent() { + + boolean exists = false; + for (String col: clients[0].getDatabase(Context.accountId.get()+"").listCollectionNames()){ + if (getCollName().equalsIgnoreCase(col)){ + exists = true; + break; + } + }; + + if (!exists) { + clients[0].getDatabase(Context.accountId.get()+"").createCollection(getCollName()); + } + + MongoCursor cursor = instance.getMCollection().listIndexes().cursor(); + int counter = 0; + while (cursor.hasNext()) { + counter++; + cursor.next(); + } + + if (counter == 1) {//Only _id as index available + String[] fieldNames = {TestRoles.NAME}; + instance.getMCollection().createIndex(Indexes.ascending(fieldNames), new IndexOptions().unique(true)); + } + } + + public TestRoles createTestRole (String roleName, ObjectId endpointLogicalGroupId, String userName) { + int createdTs = Context.now(); + TestRoles role = new TestRoles(new ObjectId(), roleName, endpointLogicalGroupId, null,userName,createdTs, createdTs); + + this.insertOne(role); + this.getLogger().info("Created test role with name :{}, and logical group id : {}", roleName, endpointLogicalGroupId.toHexString()); + return role; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunConfigDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunConfigDao.java new file mode 100644 index 0000000000..44d4b7a41b --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunConfigDao.java @@ -0,0 +1,23 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.testing.TestingRunConfig; +import com.akto.util.Constants; +import com.akto.util.enums.GlobalEnums; +import com.akto.util.enums.MongoDBEnums; + +public class TestingRunConfigDao extends AccountsContextDao { + @Override + public String getCollName() { + return MongoDBEnums.Collection.TESTING_RUN_CONFIG.getCollectionName(); + } + + public static final TestingRunConfigDao instance = new TestingRunConfigDao(); + + private TestingRunConfigDao() {} + + @Override + public Class getClassT() { + return TestingRunConfig.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java new file mode 100644 index 0000000000..8b405e79fd --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunDao.java @@ -0,0 +1,19 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.testing.TestingRun; + +public class TestingRunDao extends AccountsContextDao { + + public static final TestingRunDao instance = new TestingRunDao(); + + @Override + public String getCollName() { + return "testing_run"; + } + + @Override + public Class getClassT() { + return TestingRun.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java new file mode 100644 index 0000000000..1e3ed3b4a2 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java @@ -0,0 +1,68 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.testing.TestingRunResult; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.model.*; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; + +import java.util.ArrayList; +import java.util.List; + +public class TestingRunResultDao extends AccountsContextDao { + + public static final TestingRunResultDao instance = new TestingRunResultDao(); + + @Override + public String getCollName() { + return "testing_run_result"; + } + + @Override + public Class getClassT() { + return TestingRunResult.class; + } + + public static Bson generateFilter(ObjectId testRunId, ApiInfo.ApiInfoKey apiInfoKey) { + return generateFilter(testRunId, apiInfoKey.getApiCollectionId(), apiInfoKey.getUrl(), apiInfoKey.getMethod().name()); + } + + public static Bson generateFilter(ObjectId testRunId, int apiCollectionId ,String url, String method) { + return Filters.and( + Filters.eq(TestingRunResult.TEST_RUN_ID, testRunId), + Filters.eq(TestingRunResult.API_INFO_KEY + "." + ApiInfo.ApiInfoKey.API_COLLECTION_ID, apiCollectionId), + Filters.eq(TestingRunResult.API_INFO_KEY + "." + ApiInfo.ApiInfoKey.URL, url), + Filters.eq(TestingRunResult.API_INFO_KEY + "." + ApiInfo.ApiInfoKey.METHOD, method) + ); + } + + public List fetchLatestTestingRunResult(ObjectId testRunResultSummaryId) { + MongoCursor cursor = instance.getMCollection().find(Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testRunResultSummaryId)) + .projection( + Projections.include( + TestingRunResult.TEST_RUN_ID, + TestingRunResult.API_INFO_KEY, + TestingRunResult.TEST_SUPER_TYPE, + TestingRunResult.TEST_SUB_TYPE, + TestingRunResult.VULNERABLE, + TestingRunResult.CONFIDENCE_PERCENTAGE, + TestingRunResult.START_TIMESTAMP, + TestingRunResult.END_TIMESTAMP, + TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID + ) + ) + .sort(Sorts.descending("_id")) + .limit(10_000) + .cursor(); + List testingRunResults = new ArrayList<>(); + while (cursor.hasNext()) { + TestingRunResult testingRunResult = cursor.next(); + testingRunResult.setHexId(testingRunResult.getId().toHexString()); + testingRunResults.add(testingRunResult); + } + + return testingRunResults; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java new file mode 100644 index 0000000000..0a4291661e --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultSummariesDao.java @@ -0,0 +1,21 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.testing.TestingRunResultSummary; + +public class TestingRunResultSummariesDao extends AccountsContextDao { + + public static final TestingRunResultSummariesDao instance = new TestingRunResultSummariesDao(); + + private TestingRunResultSummariesDao() {} + + @Override + public String getCollName() { + return "testing_run_result_summaries"; + } + + @Override + public Class getClassT() { + return TestingRunResultSummary.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingSchedulesDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingSchedulesDao.java new file mode 100644 index 0000000000..19b0b90e9b --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingSchedulesDao.java @@ -0,0 +1,19 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.testing.TestingSchedule; + +public class TestingSchedulesDao extends AccountsContextDao { + + public static final TestingSchedulesDao instance = new TestingSchedulesDao(); + + @Override + public String getCollName() { + return "testing_schedules"; + } + + @Override + public Class getClassT() { + return TestingSchedule.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/WorkflowTestResultsDao.java b/libs/dao/src/main/java/com/akto/dao/testing/WorkflowTestResultsDao.java new file mode 100644 index 0000000000..e356266b25 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/WorkflowTestResultsDao.java @@ -0,0 +1,19 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.testing.WorkflowTestResult; + +public class WorkflowTestResultsDao extends AccountsContextDao { + + public static final WorkflowTestResultsDao instance = new WorkflowTestResultsDao(); + + @Override + public String getCollName() { + return "workflow_test_results"; + } + + @Override + public Class getClassT() { + return WorkflowTestResult.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/WorkflowTestsDao.java b/libs/dao/src/main/java/com/akto/dao/testing/WorkflowTestsDao.java new file mode 100644 index 0000000000..da052ab69d --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/WorkflowTestsDao.java @@ -0,0 +1,20 @@ +package com.akto.dao.testing; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.testing.WorkflowTest; + +public class WorkflowTestsDao extends AccountsContextDao { + + public static final WorkflowTestsDao instance = new WorkflowTestsDao(); + + @Override + public String getCollName() { + return "workflow_tests"; + } + + @Override + public Class getClassT() { + return WorkflowTest.class; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing/sources/TestSourceConfigsDao.java b/libs/dao/src/main/java/com/akto/dao/testing/sources/TestSourceConfigsDao.java new file mode 100644 index 0000000000..d7c35f0cf0 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing/sources/TestSourceConfigsDao.java @@ -0,0 +1,46 @@ +package com.akto.dao.testing.sources; + +import com.akto.dao.AccountsContextDao; +import com.akto.dao.context.Context; +import com.akto.dto.testing.sources.TestSourceConfig; +import com.mongodb.client.model.Filters; +import io.swagger.models.auth.In; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.akto.util.Constants.ID; + +public class TestSourceConfigsDao extends AccountsContextDao { + + ConcurrentHashMap cacheMap = new ConcurrentHashMap<>(); + AtomicInteger lastUpdatedTs = new AtomicInteger(0); + private final int REFRESH_TIME = 10 * 60; + public static final TestSourceConfigsDao instance = new TestSourceConfigsDao(); + + private TestSourceConfigsDao() {} + + @Override + public String getCollName() { + + return "test_source_configs"; + } + + public TestSourceConfig getTestSourceConfig (String id) { + int now = Context.now(); + if (cacheMap.containsKey(id) && (now - REFRESH_TIME) < lastUpdatedTs.get()) { + return cacheMap.get(id); + } + TestSourceConfig config = this.findOne(Filters.eq(ID, id)); + cacheMap.put(id, config); + lastUpdatedTs.set(Context.now()); + return cacheMap.get(id); + } + + @Override + public Class getClassT() { + return TestSourceConfig.class; + } + + +} diff --git a/libs/dao/src/main/java/com/akto/dao/testing_run_findings/TestingRunIssuesDao.java b/libs/dao/src/main/java/com/akto/dao/testing_run_findings/TestingRunIssuesDao.java new file mode 100644 index 0000000000..0606f28d9a --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dao/testing_run_findings/TestingRunIssuesDao.java @@ -0,0 +1,21 @@ +package com.akto.dao.testing_run_findings; + +import com.akto.dao.AccountsContextDao; +import com.akto.dto.test_run_findings.TestingRunIssues; +import com.akto.util.enums.MongoDBEnums; + +public class TestingRunIssuesDao extends AccountsContextDao { + + public static final TestingRunIssuesDao instance = new TestingRunIssuesDao(); + + private TestingRunIssuesDao() {} + @Override + public String getCollName() { + return MongoDBEnums.Collection.TESTING_RUN_ISSUES.getCollectionName(); + } + + @Override + public Class getClassT() { + return TestingRunIssues.class; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/APIConfig.java b/libs/dao/src/main/java/com/akto/dto/APIConfig.java new file mode 100644 index 0000000000..9194b29815 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/APIConfig.java @@ -0,0 +1,61 @@ +package com.akto.dto; + +public class APIConfig { + + private String name; + private String userIdentifier; + private int threshold; + private int sync_threshold_count; + private int sync_threshold_time; // in seconds + + public APIConfig(String name, String userIdentifier, int threshold, int sync_threshold_count, int sync_threshold_time) { + this.userIdentifier = userIdentifier; + this.threshold = threshold; + this.sync_threshold_count = sync_threshold_count; + this.sync_threshold_time = sync_threshold_time; + this.name = name; + } + + public APIConfig() { + } + + public String getUserIdentifier() { + return userIdentifier; + } + + public void setUserIdentifier(String userIdentifier) { + this.userIdentifier = userIdentifier; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public int getSync_threshold_count() { + return sync_threshold_count; + } + + public void setSync_threshold_count(int sync_threshold_count) { + this.sync_threshold_count = sync_threshold_count; + } + + public int getSync_threshold_time() { + return sync_threshold_time; + } + + public void setSync_threshold_time(int sync_threshold_time) { + this.sync_threshold_time = sync_threshold_time; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/APISpec.java b/libs/dao/src/main/java/com/akto/dto/APISpec.java new file mode 100644 index 0000000000..131f9413e9 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/APISpec.java @@ -0,0 +1,64 @@ +package com.akto.dto; + +public class APISpec { + public enum Type { + YAML, JSON + } + + Type type; + int userId; + String filename; + String content; + int apiCollectionId; + + public APISpec() { + } + + public APISpec(Type type, int userId, String filename, String content, int apiCollectionId) { + this.type = type; + this.userId = userId; + this.filename = filename; + this.content = content; + this.apiCollectionId = apiCollectionId; + } + + public Type getType() { + return this.type; + } + + public void setType(Type type) { + this.type = type; + } + + public int getUserId() { + return this.userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public String getFilename() { + return this.filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + + public int getApiCollectionId() { + return this.apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/Account.java b/libs/dao/src/main/java/com/akto/dto/Account.java new file mode 100644 index 0000000000..6eb0b3a89c --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/Account.java @@ -0,0 +1,47 @@ +package com.akto.dto; + +public class Account { + private int id; + private String name; + private boolean isDefault = false; + private String timezone = "US/Pacific"; + + public Account() {} + + public Account(int id, String name) { + this.id = id; + this.name = name; + } + + public boolean isDefault() { + return isDefault; + } + + public void setDefault(boolean aDefault) { + isDefault = aDefault; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTimezone() { + return timezone; + } + + public void setTimezone(String timezone) { + this.timezone = timezone; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/AccountSettings.java b/libs/dao/src/main/java/com/akto/dto/AccountSettings.java new file mode 100644 index 0000000000..e453d82462 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/AccountSettings.java @@ -0,0 +1,125 @@ +package com.akto.dto; + +import java.util.List; + +public class AccountSettings { + private int id; + public static final String PRIVATE_CIDR_LIST = "privateCidrList"; + private List privateCidrList; + public static final String REDACT_PAYLOAD = "redactPayload"; + private boolean redactPayload; + public static final String SAMPLE_DATA_COLLECTION_DROPPED = "sampleDataCollectionDropped"; + private boolean sampleDataCollectionDropped; + public static final String DASHBOARD_VERSION = "dashboardVersion"; + private String dashboardVersion; + public static final String API_RUNTIME_VERSION = "apiRuntimeVersion"; + private String apiRuntimeVersion; + public static final String SETUP_TYPE = "setupType"; + private SetupType setupType = SetupType.PROD; + + public static final String CENTRAL_KAFKA_IP = "centralKafkaIp"; + private String centralKafkaIp; + + public static final String AKTO_IGNORE_FLAG = "x-akto-ignore"; + + public static final String MERGE_ASYNC_OUTSIDE = "mergeAsyncOutside"; + private boolean mergeAsyncOutside; + + public AccountSettings() { + } + + public AccountSettings(int id, List privateCidrList, Boolean redactPayload, SetupType setupType) { + this.id = id; + this.privateCidrList = privateCidrList; + this.redactPayload = redactPayload; + this.setupType = setupType; + } + + public enum SetupType { + PROD, QA, STAGING, DEV + } + + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public List getPrivateCidrList() { + return privateCidrList; + } + + public void setPrivateCidrList(List privateCidrList) { + this.privateCidrList = privateCidrList; + } + + public boolean isRedactPayload() { + return redactPayload; + } + + public boolean getRedactPayload() { + return redactPayload; + } + + public void setRedactPayload(boolean redactPayload) { + this.redactPayload = redactPayload; + } + + public boolean isSampleDataCollectionDropped() { + return sampleDataCollectionDropped; + } + + public void setSampleDataCollectionDropped(boolean sampleDataCollectionDropped) { + this.sampleDataCollectionDropped = sampleDataCollectionDropped; + } + + public String getDashboardVersion() { + return dashboardVersion; + } + + public void setDashboardVersion(String dashboardVersion) { + this.dashboardVersion = dashboardVersion; + } + + public String getApiRuntimeVersion() { + return apiRuntimeVersion; + } + + public void setApiRuntimeVersion(String apiRuntimeVersion) { + this.apiRuntimeVersion = apiRuntimeVersion; + } + + public SetupType getSetupType() { + return setupType; + } + + public void setSetupType(SetupType setupType) { + this.setupType = setupType; + } + + public String getCentralKafkaIp() { + return centralKafkaIp; + } + + public void setCentralKafkaIp(String centralKafkaIp) { + this.centralKafkaIp = centralKafkaIp; + } + + public boolean getMergeAsyncOutside() { + return this.mergeAsyncOutside; + } + + public void setMergeAsyncOutside(boolean mergeAsyncOutside) { + this.mergeAsyncOutside = mergeAsyncOutside; + } + + public static final int DEFAULT_CENTRAL_KAFKA_BATCH_SIZE = 999900; + public static final int DEFAULT_CENTRAL_KAFKA_LINGER_MS = 60_000; + + public static final int DEFAULT_CENTRAL_KAFKA_MAX_POLL_RECORDS_CONFIG = 1_000; + public static final String DEFAULT_CENTRAL_KAFKA_TOPIC_NAME = "akto.central"; + +} diff --git a/libs/dao/src/main/java/com/akto/dto/AktoDataType.java b/libs/dao/src/main/java/com/akto/dto/AktoDataType.java new file mode 100644 index 0000000000..0bed45b51d --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/AktoDataType.java @@ -0,0 +1,55 @@ +package com.akto.dto; + +import java.util.List; + +import com.akto.dto.type.SingleTypeInfo; + +public class AktoDataType { + private String name; + private boolean sensitiveAlways; + private List sensitivePosition; + private int timestamp; + private IgnoreData ignoreData; + + public AktoDataType() { + } + public AktoDataType(String name, boolean sensitiveAlways, List sensitivePosition,int timestamp, IgnoreData ignoreData) { + this.name = name; + this.sensitiveAlways = sensitiveAlways; + this.sensitivePosition = sensitivePosition; + this.ignoreData = ignoreData; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public boolean isSensitiveAlways() { + return sensitiveAlways; + } + public boolean getSensitiveAlways() { + return sensitiveAlways; + } + public void setSensitiveAlways(boolean sensitiveAlways) { + this.sensitiveAlways = sensitiveAlways; + } + public List getSensitivePosition() { + return sensitivePosition; + } + public void setSensitivePosition(List sensitivePosition) { + this.sensitivePosition = sensitivePosition; + } + public int getTimestamp() { + return timestamp; + } + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + public IgnoreData getIgnoreData() { + return ignoreData; + } + public void setIgnoreData(IgnoreData ignoreData) { + this.ignoreData = ignoreData; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/ApiCollection.java b/libs/dao/src/main/java/com/akto/dto/ApiCollection.java new file mode 100644 index 0000000000..56b8a6c0a0 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/ApiCollection.java @@ -0,0 +1,130 @@ +package com.akto.dto; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import org.bson.codecs.pojo.annotations.BsonId; +import org.bson.codecs.pojo.annotations.BsonIgnore; + +import com.akto.dao.context.Context; + +public class ApiCollection { + + @BsonId + int id; + public static final String NAME = "name"; + String name; + int startTs; + Set urls; + String hostName; + public static final String HOST_NAME = "hostName"; + int vxlanId; + + @BsonIgnore + int urlsCount; + public static final String VXLAN_ID = "vxlanId"; + + public ApiCollection() { + } + + public ApiCollection(int id, String name, int startTs, Set urls, String hostName, int vxlanId) { + this.id = id; + this.name = name; + this.startTs = startTs; + this.urls = urls; + this.hostName = hostName; + this.vxlanId = vxlanId; + } + + public static boolean useHost = Objects.equals(System.getenv("USE_HOSTNAME"), "true"); + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public int getStartTs() { + return this.startTs; + } + + public void setStartTs(int startTs) { + this.startTs = startTs; + } + + public Set getUrls() { + return this.urls; + } + + public void setUrls(Set urls) { + this.urls = urls; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", name='" + getName() + "'" + + ", startTs='" + getStartTs() + "'" + + ", urls='" + getUrls() + "'" + + "}"; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public int getVxlanId() { + return vxlanId; + } + + public void setVxlanId(int vxlanId) { + this.vxlanId = vxlanId; + } + + public int getUrlsCount() { + return urlsCount; + } + + public void setUrlsCount(int urlsCount) { + this.urlsCount = urlsCount; + } + + // to be used in front end + public String getDisplayName() { + String result; + if (this.hostName != null) { + result = this.hostName + " - " + this.name; + } else { + result = this.name + ""; + } + + if (this.hostName == null || this.name == null) { + result = result.replace(" - ", ""); + } + + result = result.replace("null", ""); + return result; + } + + // To be called if you are creating a collection that is not from mirroring + public static ApiCollection createManualCollection(int id, String name){ + return new ApiCollection(id, name, Context.now() , new HashSet<>(), null, 0); + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/ApiInfo.java b/libs/dao/src/main/java/com/akto/dto/ApiInfo.java new file mode 100644 index 0000000000..cd15897e49 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/ApiInfo.java @@ -0,0 +1,250 @@ +package com.akto.dto; + +import com.akto.dao.context.Context; +import com.akto.dto.type.URLMethods; +import org.bson.codecs.pojo.annotations.BsonIgnore; + +import java.util.*; + +public class ApiInfo { + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // WHENEVER NEW FIELD IS ADDED MAKE SURE TO UPDATE getUpdates METHOD OF AktoPolicy.java AND MERGE METHOD OF + // ApiInfo.java + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + private ApiInfoKey id; + public static final String ALL_AUTH_TYPES_FOUND = "allAuthTypesFound"; + private Set> allAuthTypesFound; + + // this annotation makes sures that data is not stored in mongo + @BsonIgnore + private List actualAuthType; + + public static final String API_ACCESS_TYPES = "apiAccessTypes"; + private Set apiAccessTypes; + public static final String VIOLATIONS = "violations"; + private Map violations; + public static final String LAST_SEEN = "lastSeen"; + private int lastSeen; + + public enum AuthType { + UNAUTHENTICATED, BASIC, AUTHORIZATION_HEADER, JWT, API_TOKEN, BEARER, CUSTOM + } + + public enum ApiAccessType { + PUBLIC, PRIVATE + } + + public static class ApiInfoKey { + public static final String API_COLLECTION_ID = "apiCollectionId"; + int apiCollectionId; + public static final String URL = "url"; + public String url; + public static final String METHOD = "method"; + public URLMethods.Method method; + + public ApiInfoKey() { + } + + public ApiInfoKey(int apiCollectionId, String url, URLMethods.Method method) { + this.apiCollectionId = apiCollectionId; + this.url = url; + this.method = method; + } + + public int getApiCollectionId() { + return apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public URLMethods.Method getMethod() { + return method; + } + + public void setMethod(URLMethods.Method method) { + this.method = method; + } + + @Override + public int hashCode() { + return Objects.hash(url, method, apiCollectionId); + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof ApiInfoKey)) { + return false; + } + ApiInfoKey apiInfoKey= (ApiInfoKey) o; + return + url.equals(apiInfoKey.url) && method.equals(apiInfoKey.method) + && apiCollectionId == apiInfoKey.apiCollectionId; + } + + + @Override + public String toString() { + return apiCollectionId + " " + url + " " + method; + } + + public static ApiInfoKey generateFromHttpResponseParams(HttpResponseParams httpResponseParams) { + return new ApiInfo.ApiInfoKey( + httpResponseParams.getRequestParams().getApiCollectionId(), + httpResponseParams.getRequestParams().getURL(), + URLMethods.Method.fromString(httpResponseParams.getRequestParams().getMethod()) + ); + } + + } + + + public ApiInfo() { } + + public ApiInfo(int apiCollectionId, String url, URLMethods.Method method) { + this(new ApiInfoKey(apiCollectionId, url, method)); + } + + public ApiInfo(ApiInfoKey apiInfoKey) { + this.id = apiInfoKey; + this.violations = new HashMap<>(); + this.apiAccessTypes = new HashSet<>(); + this.allAuthTypesFound = new HashSet<>(); + this.lastSeen = Context.now(); + } + + public ApiInfo(HttpResponseParams httpResponseParams) { + this( + httpResponseParams.getRequestParams().getApiCollectionId(), + httpResponseParams.getRequestParams().getURL(), + URLMethods.Method.fromString(httpResponseParams.getRequestParams().getMethod()) + ); + } + + public Map getViolations() { + return violations; + } + + public void updateCustomFields(Map updatedFields) { + for (String key: updatedFields.keySet()) { + this.violations.put(key, updatedFields.get(key)); + } + } + + public void merge(ApiInfo that) { + // never merge id + if (that.lastSeen > this.lastSeen) { + this.lastSeen = that.lastSeen; + } + + for (String k: that.violations.keySet()) { + if (this.violations.get(k) == null || that.violations.get(k) > this.violations.get(k)) { + this.violations.put(k,that.violations.get(k)); + } + } + + this.allAuthTypesFound.addAll(that.allAuthTypesFound); + + this.apiAccessTypes.addAll(that.getApiAccessTypes()); + + } + + public void setViolations(Map violations) { + this.violations = violations; + } + + private static String mainKey(String url, URLMethods.Method method, int apiCollectionId) { + return url + "," + method + "," + apiCollectionId; + } + + public String key() { + return mainKey(this.id.url, this.id.method, this.id.apiCollectionId); + } + + public static String keyFromHttpResponseParams(HttpResponseParams httpResponseParams) { + return mainKey( + httpResponseParams.getRequestParams().getURL(), + URLMethods.Method.fromString(httpResponseParams.getRequestParams().getMethod()), + httpResponseParams.getRequestParams().getApiCollectionId() + ); + } + + public void calculateActualAuth() { + List result = new ArrayList<>(); + for (Set authTypes: this.allAuthTypesFound) { + if (authTypes.contains(AuthType.UNAUTHENTICATED)) { + this.actualAuthType = Collections.singletonList(AuthType.UNAUTHENTICATED); + return; + } + if (authTypes.size() == 1) { + result.addAll(authTypes); + } + } + + this.actualAuthType = result; + + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", allAuthTypesFound='" + getAllAuthTypesFound() + "'" + + ", lastSeen='" + getLastSeen() + "'" + + ", violations='" + getViolations() + "'" + + ", accessTypes='" + getApiAccessTypes() + "'" + + "}"; + } + + + public ApiInfoKey getId() { + return id; + } + + public void setId(ApiInfoKey id) { + this.id = id; + } + + public Set> getAllAuthTypesFound() { + return allAuthTypesFound; + } + + public void setAllAuthTypesFound(Set> allAuthTypesFound) { + this.allAuthTypesFound = allAuthTypesFound; + } + + public Set getApiAccessTypes() { + return apiAccessTypes; + } + + public void setApiAccessTypes(Set apiAccessTypes) { + this.apiAccessTypes = apiAccessTypes; + } + + public List getActualAuthType() { + return actualAuthType; + } + + public void setActualAuthType(List actualAuthType) { + this.actualAuthType = actualAuthType; + } + + public int getLastSeen() { + return lastSeen; + } + + public void setLastSeen(int lastSeen) { + this.lastSeen = lastSeen; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/ApiInfoCatalog.java b/libs/dao/src/main/java/com/akto/dto/ApiInfoCatalog.java new file mode 100644 index 0000000000..0febe2238a --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/ApiInfoCatalog.java @@ -0,0 +1,47 @@ +package com.akto.dto; + +import com.akto.dto.type.URLStatic; +import com.akto.dto.type.URLTemplate; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ApiInfoCatalog { + private Map strictURLToMethods; + private Map templateURLToMethods; + private List deletedInfo = new ArrayList<>(); + + public ApiInfoCatalog() { + } + + public ApiInfoCatalog(Map strictURLToMethods, Map templateURLToMethods, List deletedInfo) { + this.strictURLToMethods = strictURLToMethods; + this.templateURLToMethods = templateURLToMethods; + this.deletedInfo = deletedInfo; + } + + public Map getStrictURLToMethods() { + return strictURLToMethods; + } + + public void setStrictURLToMethods(Map strictURLToMethods) { + this.strictURLToMethods = strictURLToMethods; + } + + public Map getTemplateURLToMethods() { + return templateURLToMethods; + } + + public void setTemplateURLToMethods(Map templateURLToMethods) { + this.templateURLToMethods = templateURLToMethods; + } + + public List getDeletedInfo() { + return deletedInfo; + } + + public void setDeletedInfo(List deletedInfo) { + this.deletedInfo = deletedInfo; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/ApiToken.java b/libs/dao/src/main/java/com/akto/dto/ApiToken.java new file mode 100644 index 0000000000..857e2c9225 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/ApiToken.java @@ -0,0 +1,105 @@ +package com.akto.dto; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class ApiToken { + private int id; + private int accountId; + private String name; + private String key; + public static final String KEY ="key"; + private int timestamp; + private String username; + public static final String USER_NAME = "username"; + private Utility utility; + public static final String UTILITY = "utility"; + + public static final String FULL_STRING_ALLOWED_API = "*"; + + public enum Utility{ + BURP(Arrays.asList("/api/uploadHar", "/api/importInBurp", "/api/sendHealthCheck")), + EXTERNAL_API(Collections.singletonList(FULL_STRING_ALLOWED_API)), + SLACK(Collections.emptyList()); + + private final List accessList; + + Utility(List accessList) { + this.accessList = accessList; + } + + public List getAccessList() { + return accessList; + } + } + + public ApiToken() {} + + + public ApiToken(int id,int accountId,String name,String key, int timestamp, String username, Utility utility) { + this.id = id; + this.accountId = accountId; + this.name = name; + this.key = key; + this.timestamp = timestamp; + this.username = username; + this.utility = utility; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public int getTimestamp() { + return timestamp; + } + + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Utility getUtility() { + return utility; + } + + public void setUtility(Utility utility) { + this.utility = utility; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAccountId() { + return accountId; + } + + public void setAccountId(int accountId) { + this.accountId = accountId; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/Attempt.java b/libs/dao/src/main/java/com/akto/dto/Attempt.java new file mode 100644 index 0000000000..c1babbee51 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/Attempt.java @@ -0,0 +1,229 @@ +package com.akto.dto; + +import java.util.Map; +import java.util.UUID; + +import com.akto.dao.context.Context; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; +import org.bson.codecs.pojo.annotations.BsonId; + +import java.util.List; + +public class Attempt { + + @BsonDiscriminator + public static abstract class AttemptResult {} + + @BsonDiscriminator + public static class Success extends AttemptResult { + private Map> requestHeaders; + private String requestBody; + + private Map> responseHeaders; + private String responseBody; + private int errorCode; + private int timeTakenInMillis; + + public Success() {} + + public Success(Map> requestHeaders, String requestBody, Map> responseHeaders, String responseBody, int errorCode, int timeTakenInMillis) { + this.requestHeaders = requestHeaders; + this.requestBody = requestBody; + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + this.errorCode = errorCode; + this.timeTakenInMillis = timeTakenInMillis; + } + + public Map> getRequestHeaders() { + return this.requestHeaders; + } + + public void setRequestHeaders(Map> requestHeaders) { + this.requestHeaders = requestHeaders; + } + + public String getRequestBody() { + return this.requestBody; + } + + public void setRequestBody(String requestBody) { + this.requestBody = requestBody; + } + + public Map> getResponseHeaders() { + return this.responseHeaders; + } + + public void setResponseHeaders(Map> responseHeaders) { + this.responseHeaders = responseHeaders; + } + + public String getResponseBody() { + return this.responseBody; + } + + public void setResponseBody(String responseBody) { + this.responseBody = responseBody; + } + + public int getErrorCode() { + return this.errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public int getTimeTakenInMillis() { + return this.timeTakenInMillis; + } + + public void setTimeTakenInMillis(int timeTakenInMillis) { + this.timeTakenInMillis = timeTakenInMillis; + } + + @Override + public String toString() { + return "{" + + " requestBody='" + getRequestBody()+ "'" + + " responseBody='" + getResponseBody() + "'" + + " errorCode='" + getErrorCode() + "'" + + " timeTakenInMillis='" + getTimeTakenInMillis() + "'" + + "}"; + } + } + + @BsonDiscriminator + public static class Err extends AttemptResult { + + private String errorMessage; + + public Err() {} + + public Err(String errorMessage) { + this.errorMessage = errorMessage; + } + + public String getErrorMessage() { + return this.errorMessage; + } + + public void setErrorMessage(String errorMessage){ + this.errorMessage = errorMessage; + } + + @Override + public String toString() { + return "{" + + " errorMessage='" + getErrorMessage() + "'" + + "}"; + } + } + + public enum Status { + CREATED, NOT_CREATED, FAILED, SENT; + } + + private int timestamp; + + @BsonId + private String id; + private String uri; + private String method; + private AttemptResult attemptResult; + private String status; + private boolean isHappy; + + public Attempt() { + } + + public Attempt(int timestamp, String id, String uri, String method, AttemptResult attemptResult, boolean isHappy) { + this.timestamp = timestamp; + this.id = id; + this.uri = uri; + this.method = method; + this.attemptResult = attemptResult; + this.isHappy = isHappy; + if (attemptResult instanceof Err) { + this.status = Status.NOT_CREATED.toString(); + } else { + this.status = Status.CREATED.toString(); + } + } + + public Attempt(String uri, String method, AttemptResult attemptResult, boolean isHappy) { + this (Context.now(), UUID.randomUUID().toString(), uri, method, attemptResult, isHappy); + } + + public int getTimestamp() { + return this.timestamp; + } + + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUri() { + return this.uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getMethod() { + return this.method; + } + + public void setMethod(String method) { + this.method = method; + } + + public AttemptResult getAttemptResult() { + return this.attemptResult; + } + + public void setAttemptResult(AttemptResult attemptResult) { + this.attemptResult = attemptResult; + } + + public String getStatus() { + return this.status.toString(); + } + + public void setStatus(String status) { + this.status = status; + } + + public boolean getIsHappy() { + return this.isHappy; + } + + public void setIsHappy(boolean isHappy) { + this.isHappy = isHappy; + } + + @Override + public String toString() { + return "{" + + " timestamp='" + getTimestamp() + "'" + + ", uuid='" + getId() + "'" + + ", uri='" + getUri() + "'" + + ", method='" + getMethod() + "'" + + ", status='" + getStatus() + "'" + + ", isHappy='" + getIsHappy() + "'" + + ", attemptResult='" + getAttemptResult() + "'" + + "}"; + } +} + diff --git a/libs/dao/src/main/java/com/akto/dto/AwsResource.java b/libs/dao/src/main/java/com/akto/dto/AwsResource.java new file mode 100644 index 0000000000..6aef0533de --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/AwsResource.java @@ -0,0 +1,40 @@ +package com.akto.dto; + +public class AwsResource { + // private AwsResourceType resourceType; + private String resourceName; + private String resourceId; + + public AwsResource() { + } + + public AwsResource(String resourceName, String resourceId) { + this.resourceName = resourceName; + this.resourceId = resourceId; + // this.resourceType = awsResourceType; + } + + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + public String getResourceName() { + return this.resourceName; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getResourceId() { + return this.resourceId; + } + + // public void setAwsResourceType(AwsResourceType resourceType) { + // this.resourceType = resourceType; + // } + + // public AwsResourceType getAwsResourceType() { + // return this.resourceType; + // } +} diff --git a/libs/dao/src/main/java/com/akto/dto/AwsResources.java b/libs/dao/src/main/java/com/akto/dto/AwsResources.java new file mode 100644 index 0000000000..63f101c375 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/AwsResources.java @@ -0,0 +1,37 @@ +package com.akto.dto; + +import java.util.List; + +public class AwsResources { + // private EnumMap> awsResources; + private int id; + private List loadBalancers; + + public AwsResources() { + } + + public AwsResources(int id, List loadBalancers) { + this.id = id; + this.loadBalancers = loadBalancers; + } + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + public List getLoadBalancers() { + return this.loadBalancers; + } + + public void setLoadBalancers(List loadBalancers) { + this.loadBalancers = loadBalancers; + } +} + +// enum AwsResourceType { +// LoadBalancer, EC2; +// } diff --git a/libs/dao/src/main/java/com/akto/dto/BackwardCompatibility.java b/libs/dao/src/main/java/com/akto/dto/BackwardCompatibility.java new file mode 100644 index 0000000000..a2c6cb1cec --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/BackwardCompatibility.java @@ -0,0 +1,140 @@ +package com.akto.dto; + +public class BackwardCompatibility { + private int id; + + public static final String DROP_FILTER_SAMPLE_DATA = "dropFilterSampleData"; + private int dropFilterSampleData; + private int resetSingleTypeInfoCount; + public static final String RESET_SINGLE_TYPE_INFO_COUNT = "resetSingleTypeInfoCount"; + + public static final String DROP_WORKFLOW_TEST_RESULT = "dropWorkflowTestResult"; + private int dropWorkflowTestResult; + + public static final String READY_FOR_NEW_TESTING_FRAMEWORK = "readyForNewTestingFramework"; + private int readyForNewTestingFramework; + + public static final String ADD_AKTO_DATA_TYPES = "addAktoDataTypes"; + private int addAktoDataTypes; + + public static final String MERGE_ON_HOST_INIT = "mergeOnHostInit"; + private int mergeOnHostInit; + + public static final String DEPLOYMENT_STATUS_UPDATED = "deploymentStatusUpdated"; + private boolean deploymentStatusUpdated; + + public static final String AUTH_MECHANISM_DATA = "authMechanismData"; + private int authMechanismData; + + public static final String MIRRORING_LAMBDA_TRIGGERED = "mirroringLambdaTriggered"; + private boolean mirroringLambdaTriggered; + + public static final String DELETE_ACCESS_LIST_FROM_API_TOKEN = "deleteAccessListFromApiToken"; + private int deleteAccessListFromApiToken; + + public BackwardCompatibility(int id, int dropFilterSampleData, int resetSingleTypeInfoCount, int dropWorkflowTestResult, + int readyForNewTestingFramework,int addAktoDataTypes, boolean deploymentStatusUpdated, + int authMechanismData, boolean mirroringLambdaTriggered, int deleteAccessListFromApiToken) { + this.id = id; + this.dropFilterSampleData = dropFilterSampleData; + this.resetSingleTypeInfoCount = resetSingleTypeInfoCount; + this.dropWorkflowTestResult = dropWorkflowTestResult; + this.readyForNewTestingFramework = readyForNewTestingFramework; + this.addAktoDataTypes = addAktoDataTypes; + this.deploymentStatusUpdated = deploymentStatusUpdated; + this.mirroringLambdaTriggered = mirroringLambdaTriggered; + this.authMechanismData = authMechanismData; + this.deleteAccessListFromApiToken = deleteAccessListFromApiToken; + } + + public BackwardCompatibility() { + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getDropFilterSampleData() { + return dropFilterSampleData; + } + + public void setDropFilterSampleData(int dropFilterSampleData) { + this.dropFilterSampleData = dropFilterSampleData; + } + + public int getResetSingleTypeInfoCount() { + return resetSingleTypeInfoCount; + } + + public void setResetSingleTypeInfoCount(int resetSingleTypeInfoCount) { + this.resetSingleTypeInfoCount = resetSingleTypeInfoCount; + } + + public int getDropWorkflowTestResult() { + return dropWorkflowTestResult; + } + + public void setDropWorkflowTestResult(int dropWorkflowTestResult) { + this.dropWorkflowTestResult = dropWorkflowTestResult; + } + + public int getReadyForNewTestingFramework() { + return this.readyForNewTestingFramework; + } + + public void setReadyForNewTestingFramework(int readyForNewTestingFramework) { + this.readyForNewTestingFramework = readyForNewTestingFramework; + } + + public int getAddAktoDataTypes() { + return addAktoDataTypes; + } + + public void setAddAktoDataTypes(int addAktoDataTypes) { + this.addAktoDataTypes = addAktoDataTypes; + } + + public int getMergeOnHostInit() { + return this.mergeOnHostInit; + } + + public void setMergeOnHostInit(int mergeOnHostInit) { + this.mergeOnHostInit = mergeOnHostInit; + } + + public boolean isDeploymentStatusUpdated() { + return deploymentStatusUpdated; + } + + public void setDeploymentStatusUpdated(boolean deploymentStatusUpdated) { + this.deploymentStatusUpdated = deploymentStatusUpdated; + } + + public boolean isMirroringLambdaTriggered() { + return mirroringLambdaTriggered; + } + + public void setMirroringLambdaTriggered(boolean mirroringLambdaTriggered) { + this.mirroringLambdaTriggered = mirroringLambdaTriggered; + } + + public int getAuthMechanismData() { + return this.authMechanismData; + } + + public void setAuthMechanismData(int authMechanismData) { + this.authMechanismData = authMechanismData; + } + + public int getDeleteAccessListFromApiToken() { + return deleteAccessListFromApiToken; + } + + public void setDeleteAccessListFromApiToken(int deleteAccessListFromApiToken) { + this.deleteAccessListFromApiToken = deleteAccessListFromApiToken; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/BurpPluginInfo.java b/libs/dao/src/main/java/com/akto/dto/BurpPluginInfo.java new file mode 100644 index 0000000000..a6f70fbd7b --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/BurpPluginInfo.java @@ -0,0 +1,61 @@ +package com.akto.dto; + +public class BurpPluginInfo { + + public static final String USERNAME = "username"; + private String username; + public static final String LAST_BOOT_UP_TIMESTAMP = "lastBootupTimestamp"; + private int lastBootupTimestamp; + public static final String LAST_DATA_SENT_TIMESTAMP = "lastDataSentTimestamp"; + private int lastDataSentTimestamp; + public static final String VERSION = "version"; + private int version; + public static final String LAST_DOWNLOAD_TIMESTAMP = "lastDownloadTimestamp"; + public int lastDownloadTimestamp; + + public BurpPluginInfo() { + + } + + public BurpPluginInfo(String username, int lastBootupTimestamp, int lastDataSentTimestamp, int version) { + this.username = username; + this.lastBootupTimestamp = lastBootupTimestamp; + this.lastDataSentTimestamp = lastDataSentTimestamp; + this.version = version; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public int getLastBootupTimestamp() { + return lastBootupTimestamp; + } + + public void setLastBootupTimestamp(int lastBootupTimestamp) { + this.lastBootupTimestamp = lastBootupTimestamp; + } + + public int getLastDataSentTimestamp() { + return lastDataSentTimestamp; + } + + public void setLastDataSentTimestamp(int lastDataSentTimestamp) { + this.lastDataSentTimestamp = lastDataSentTimestamp; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + + +} diff --git a/libs/dao/src/main/java/com/akto/dto/Config.java b/libs/dao/src/main/java/com/akto/dto/Config.java new file mode 100644 index 0000000000..f0e2226213 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/Config.java @@ -0,0 +1,216 @@ +package com.akto.dto; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +@BsonDiscriminator +public abstract class Config { + public ConfigType getConfigType() { + return configType; + } + + public void setConfigType(ConfigType configType) { + this.configType = configType; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + String id; + + public enum ConfigType { + SLACK, GOOGLE, WEBPUSH, PASSWORD, SALESFORCE; + } + + ConfigType configType; + + @BsonDiscriminator + public static class SlackConfig extends Config { + String clientId; + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getRedirect_url() { + return redirect_url; + } + + public void setRedirect_url(String redirect_url) { + this.redirect_url = redirect_url; + } + + String clientSecret; + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + String redirect_url; + + public SlackConfig() { + this.configType = ConfigType.SLACK; + this.id = configType.name()+"-ankush"; + } + } + + @BsonDiscriminator + public static class GoogleConfig extends Config { + + String clientId, projectId, authURI, tokenURI, certURL, clientSecret, jsOrigins; + + public GoogleConfig() { + this.configType = ConfigType.GOOGLE; + this.id = configType.name()+"-ankush"; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getAuthURI() { + return authURI; + } + + public void setAuthURI(String authURI) { + this.authURI = authURI; + } + + public String getTokenURI() { + return tokenURI; + } + + public void setTokenURI(String tokenURI) { + this.tokenURI = tokenURI; + } + + public String getCertURL() { + return certURL; + } + + public void setCertURL(String certURL) { + this.certURL = certURL; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getJsOrigins() { + return jsOrigins; + } + + public void setJsOrigins(String jsOrigins) { + this.jsOrigins = jsOrigins; + } + } + + @BsonDiscriminator + public static class WebpushConfig extends Config { + + String publicKey; + String privateKey; + + public WebpushConfig() { + this.configType = ConfigType.WEBPUSH; + this.id = configType.name()+"-ankush"; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + + } + + @BsonDiscriminator + public static class SalesforceConfig extends Config { + String consumer_key,consumer_secret, redirect_uri, response_type; + + public SalesforceConfig() { + this.configType = ConfigType.SALESFORCE; + this.id = configType.name() + "-ankush"; + } + + public SalesforceConfig(String consumer_key,String consumer_secret, String redirect_uri, String response_type) { + this.configType = ConfigType.SALESFORCE; + this.id = configType.name() + "-ankush"; + this.consumer_key = consumer_key; + this.consumer_secret = consumer_secret; + this.redirect_uri = redirect_uri; + this.response_type = response_type; + } + + public String getConsumer_key() { + return consumer_key; + } + + public void setConsumer_key(String consumer_key) { + this.consumer_key = consumer_key; + } + + public String getConsumer_secret() { + return consumer_secret; + } + + public void setConsumer_secret(String consumer_secret) { + this.consumer_secret = consumer_secret; + } + + public String getRedirect_uri() { + return redirect_uri; + } + + public void setRedirect_uri(String redirect_uri) { + this.redirect_uri = redirect_uri; + } + + public String getResponse_type() { + return response_type; + } + + public void setResponse_type(String response_type) { + this.response_type = response_type; + } + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/CustomAuthType.java b/libs/dao/src/main/java/com/akto/dto/CustomAuthType.java new file mode 100644 index 0000000000..9897e2e69d --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/CustomAuthType.java @@ -0,0 +1,78 @@ +package com.akto.dto; + +import java.util.List; + +import org.bson.types.ObjectId; + +import com.akto.dao.context.Context; + +public class CustomAuthType { + private ObjectId id; + public static final String NAME = "name"; + private String name; + private List headerKeys; + private List payloadKeys; + public static final String ACTIVE = "active"; + private boolean active; + private int creatorId; + private int timestamp; + + public CustomAuthType() { + } + public CustomAuthType(String name, List headerKeys, List payloadKeys, boolean active, int creatorId) { + this.name = name; + this.headerKeys = headerKeys; + this.payloadKeys = payloadKeys; + this.active = active; + this.creatorId = creatorId; + this.timestamp = Context.now(); + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public List getHeaderKeys() { + return headerKeys; + } + public void setHeaderKeys(List headerKeys) { + this.headerKeys = headerKeys; + } + public List getPayloadKeys() { + return payloadKeys; + } + public void setPayloadKeys(List payloadKeys) { + this.payloadKeys = payloadKeys; + } + public boolean isActive() { + return active; + } + public boolean getActive() { + return active; + } + public void setActive(boolean active) { + this.active = active; + } + public String generateName(){ + return String.join("_",this.name); + } + public int getCreatorId() { + return creatorId; + } + public void setCreatorId(int creatorId) { + this.creatorId = creatorId; + } + public int getTimestamp() { + return timestamp; + } + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + public ObjectId getId() { + return id; + } + public void setId(ObjectId id) { + this.id = id; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/CustomDataType.java b/libs/dao/src/main/java/com/akto/dto/CustomDataType.java new file mode 100644 index 0000000000..fd90e19802 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/CustomDataType.java @@ -0,0 +1,188 @@ +package com.akto.dto; + +import com.akto.dao.context.Context; +import com.akto.dto.data_types.Conditions; +import com.akto.dto.type.SingleTypeInfo; +import io.swagger.v3.oas.models.media.StringSchema; +import org.bson.types.ObjectId; + +import java.util.List; +import java.util.Objects; + +public class CustomDataType { + private ObjectId id; + public static final String NAME = "name"; + private String name; + public static final String SENSITIVE_ALWAYS = "sensitiveAlways"; + private boolean sensitiveAlways; + public static final String SENSITIVE_POSITION = "sensitivePosition"; + private List sensitivePosition; + private int creatorId; + public static final String TIMESTAMP = "timestamp"; + private int timestamp; + public static final String ACTIVE = "active"; + private boolean active; + + public static final String KEY_CONDITIONS = "keyConditions"; + Conditions keyConditions; + public static final String VALUE_CONDITIONS = "valueConditions"; + Conditions valueConditions; + public static final String OPERATOR = "operator"; + Conditions.Operator operator; + public static final String IGNORE_DATA = "ignoreData"; + private IgnoreData ignoreData; + + public CustomDataType() { } + + public CustomDataType(String name, boolean sensitiveAlways, List sensitivePosition, int creatorId, boolean active, Conditions keyConditions, Conditions valueConditions, Conditions.Operator operator, IgnoreData ignoreData) { + this.name = name; + this.sensitiveAlways = sensitiveAlways; + this.sensitivePosition = sensitivePosition; + this.creatorId = creatorId; + this.timestamp = Context.now(); + this.active = active; + this.keyConditions = keyConditions; + this.valueConditions = valueConditions; + this.operator = operator; + this.ignoreData = ignoreData; + } + + public SingleTypeInfo.SubType toSubType() { + return new SingleTypeInfo.SubType( + this.name,this.sensitiveAlways, SingleTypeInfo.SuperType.CUSTOM, + StringSchema.class, this.sensitivePosition + ); + } + + public boolean validate(Object value, Object key) { + if (this.keyConditions == null && this.valueConditions==null) return false; + boolean keyResult = true; + if (this.keyConditions != null) { + keyResult = this.keyConditions.validate(key); + } + + boolean valueResult = true; + if (this.valueConditions != null) { + valueResult = this.valueConditions.validate(value); + } + + if (this.valueConditions ==null || this.keyConditions == null) { + return keyResult && valueResult; + } else { + switch (this.operator) { + case AND: + return keyResult && valueResult; + case OR: + return keyResult || valueResult; + default: + return false; + } + } + + } + + + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public Conditions getKeyConditions() { + return keyConditions; + } + + public void setKeyConditions(Conditions keyConditions) { + this.keyConditions = keyConditions; + } + + public Conditions getValueConditions() { + return valueConditions; + } + + public void setValueConditions(Conditions valueConditions) { + this.valueConditions = valueConditions; + } + + public Conditions.Operator getOperator() { + return operator; + } + + public void setOperator(Conditions.Operator operator) { + this.operator = operator; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isSensitiveAlways() { + return sensitiveAlways; + } + + public void setSensitiveAlways(boolean sensitiveAlways) { + this.sensitiveAlways = sensitiveAlways; + } + + public List getSensitivePosition() { + return sensitivePosition; + } + + public void setSensitivePosition(List sensitivePosition) { + this.sensitivePosition = sensitivePosition; + } + + public int getCreatorId() { + return creatorId; + } + + public void setCreatorId(int creatorId) { + this.creatorId = creatorId; + } + + public int getTimestamp() { + return timestamp; + } + + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public IgnoreData getIgnoreData() { + return ignoreData; + } + + public void setIgnoreData(IgnoreData ignoreData) { + this.ignoreData = ignoreData; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", name='" + getName() + "'" + + ", sensitiveAlways='" + isSensitiveAlways() + "'" + + ", sensitivePosition='" + getSensitivePosition() + "'" + + ", creatorId='" + getCreatorId() + "'" + + ", timestamp='" + getTimestamp() + "'" + + ", active='" + isActive() + "'" + + ", keyConditions='" + getKeyConditions() + "'" + + ", valueConditions='" + getValueConditions() + "'" + + ", operator='" + getOperator() + "'" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/CustomFilter.java b/libs/dao/src/main/java/com/akto/dto/CustomFilter.java new file mode 100644 index 0000000000..9e60dba4a3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/CustomFilter.java @@ -0,0 +1,11 @@ +package com.akto.dto; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +@BsonDiscriminator +public abstract class CustomFilter { + public CustomFilter() { + } + + public abstract boolean process(HttpResponseParams httpResponseParams); +} diff --git a/libs/dao/src/main/java/com/akto/dto/Dibs.java b/libs/dao/src/main/java/com/akto/dto/Dibs.java new file mode 100644 index 0000000000..c771a6c629 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/Dibs.java @@ -0,0 +1,89 @@ +package com.akto.dto; + +import org.bson.codecs.pojo.annotations.BsonId; + +public class Dibs { + + @BsonId + private String id; + + private int startTs; + private int expiryTs; + private int freqInSeconds; + private int lastPing; + private String winner; + + + public Dibs() { + } + + public Dibs(String id, int startTs, int expiryTs, int freqInSeconds, int lastPing, String winner) { + this.id = id; + this.startTs = startTs; + this.expiryTs = expiryTs; + this.freqInSeconds = freqInSeconds; + this.lastPing = lastPing; + this.winner = winner; + } + + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + public int getStartTs() { + return this.startTs; + } + + public void setStartTs(int startTs) { + this.startTs = startTs; + } + + public int getExpiryTs() { + return this.expiryTs; + } + + public void setExpiryTs(int expiryTs) { + this.expiryTs = expiryTs; + } + + public int getFreqInSeconds() { + return this.freqInSeconds; + } + + public void setFreqInSeconds(int freqInSeconds) { + this.freqInSeconds = freqInSeconds; + } + + public int getLastPing() { + return this.lastPing; + } + + public void setLastPing(int lastPing) { + this.lastPing = lastPing; + } + + public String getWinner() { + return this.winner; + } + + public void setWinner(String winner) { + this.winner = winner; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", startTs='" + getStartTs() + "'" + + ", expiryTs='" + getExpiryTs() + "'" + + ", freqInSeconds='" + getFreqInSeconds() + "'" + + ", lastPing='" + getLastPing() + "'" + + ", winner='" + getWinner() + "'" + + "}"; + } + +} \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dto/FilterSampleData.java b/libs/dao/src/main/java/com/akto/dto/FilterSampleData.java new file mode 100644 index 0000000000..03e205539b --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/FilterSampleData.java @@ -0,0 +1,78 @@ +package com.akto.dto; + + +import com.akto.types.CappedList; + +import java.util.ArrayList; +import java.util.List; + +public class FilterSampleData { + private FilterKey id; + public static final String SAMPLES = "samples"; + private CappedList samples; + + public static final int cap = 10; + + public FilterSampleData() { + } + + public static class FilterKey { + public ApiInfo.ApiInfoKey apiInfoKey; + public int filterId; + + public FilterKey() { + } + + public FilterKey(ApiInfo.ApiInfoKey apiInfoKey, int filterId) { + this.apiInfoKey = apiInfoKey; + this.filterId = filterId; + } + + public ApiInfo.ApiInfoKey getApiInfoKey() { + return apiInfoKey; + } + + public void setApiInfoKey(ApiInfo.ApiInfoKey apiInfoKey) { + this.apiInfoKey = apiInfoKey; + } + + public int getFilterId() { + return filterId; + } + + public void setFilterId(int filterId) { + this.filterId = filterId; + } + } + + + public FilterSampleData(ApiInfo.ApiInfoKey id, Integer filterId) { + this.id = new FilterKey(id,filterId); + this.samples = new CappedList<>(cap, true); + } + + public void merge(FilterSampleData that) { + for (String s: that.samples.get()) { + if (this.getSamples().get().size() > cap) { + return; + } + this.getSamples().get().add(s); + } + } + + public FilterKey getId() { + return id; + } + + public void setId(FilterKey id) { + this.id = id; + } + + public CappedList getSamples() { + return samples; + } + + public void setSamples(CappedList samples) { + this.samples = samples; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/HttpRequestParams.java b/libs/dao/src/main/java/com/akto/dto/HttpRequestParams.java new file mode 100644 index 0000000000..fb48d7a52e --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/HttpRequestParams.java @@ -0,0 +1,113 @@ +package com.akto.dto; + + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HttpRequestParams { + public String method; // POST + public String url; + public String type; // HTTP/1.1 + private Map> headers = new HashMap<>(); + private String payload; + private int apiCollectionId; + + public HttpRequestParams() {} + + public HttpRequestParams(String method, String url, String type, Map> headers, String payload, int apiCollectionId) { + this.method = method; + this.url = url; + this.type = type; + this.headers = headers; + this.payload = payload; + this.apiCollectionId = apiCollectionId; + + } + + public static List parseRequest(String request) throws IOException { + + List requests = new ArrayList<>(); + BufferedReader reader = new BufferedReader(new StringReader(request)); + String line = reader.readLine(); + + while (true) { + String[] tokens = line.split(" "); + HttpRequestParams httpRequestParams = new HttpRequestParams(); + httpRequestParams.method = tokens[0]; + httpRequestParams.url = tokens[1]; + httpRequestParams.type = tokens[2]; + + int contentLength = 0; + + while((line = reader.readLine()) != null) { + if (line.length() > 0 && line.charAt(0) != '{') { + tokens = line.split(": "); + List headerValues = httpRequestParams.getHeaders().get(tokens[0]); + if (headerValues == null) { + headerValues = new ArrayList<>(); + httpRequestParams.getHeaders().put(tokens[0], headerValues); + } + + headerValues.add(tokens[1]); + if (tokens[0].toLowerCase().equals("content-length")) { + contentLength = Integer.parseInt(tokens[1]); + } + + } else { + break; + } + } + + line = reader.readLine(); + + String payload = line.substring(0, contentLength); + httpRequestParams.setPayload(payload); + requests.add(httpRequestParams); + String restOfLine = line.substring(contentLength); + if (restOfLine.length() > 0) { + line = restOfLine; + } else { + break; + } + } + + return requests; + } + + public Map> getHeaders() { + return headers; + } + + public void setHeaders(Map> headers) { + this.headers = headers; + } + + public String getPayload() { + return payload; + } + + public void setPayload(String payload) { + this.payload = payload; + } + + public String getURL() { + return this.url; + } + + public String getMethod() { + return this.method; + } + + public int getApiCollectionId() { + return this.apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/HttpResponseParams.java b/libs/dao/src/main/java/com/akto/dto/HttpResponseParams.java new file mode 100644 index 0000000000..9b3445c3b3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/HttpResponseParams.java @@ -0,0 +1,109 @@ +package com.akto.dto; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HttpResponseParams { + + public enum Source { + HAR, PCAP, MIRRORING, SDK, OTHER, POSTMAN + } + + public String accountId; + public String type; // HTTP/1.1 + public int statusCode; // 200 + public String status; // OK + public Map> headers = new HashMap<>(); + private String payload; + private int time; + public HttpRequestParams requestParams; + boolean isPending; + Source source = Source.OTHER; + String orig; + String sourceIP; + + public HttpResponseParams() {} + + public HttpResponseParams(String type, int statusCode, String status, Map> headers, String payload, + HttpRequestParams requestParams, int time, String accountId, boolean isPending, Source source, + String orig, String sourceIP) { + this.type = type; + this.statusCode = statusCode; + this.status = status; + this.headers = headers; + this.payload = payload; + this.requestParams = requestParams; + this.time = time; + this.accountId = accountId; + this.isPending = isPending; + this.source = source; + this.orig = orig; + this.sourceIP = sourceIP; + } + + public static boolean validHttpResponseCode(int statusCode) { + return statusCode >= 200 && (statusCode < 300 || statusCode == 302); + } + + public String getPayload() { + return payload; + } + + public void setPayload(String payload) { + this.payload = payload; + } + + public HttpRequestParams getRequestParams() { + return this.requestParams; + } + + public int getStatusCode() { + return this.statusCode; + } + + public Map> getHeaders() { + return this.headers; + } + + public int getTime() { + return time; + } + + public String getAccountId() { + return accountId; + } + + public boolean getIsPending() { + return this.isPending; + } + + public void setIsPending(boolean isPending) { + this.isPending = isPending; + } + + public Source getSource() { + return this.source; + } + + public void setSource(Source source) { + this.source = source; + } + + public String getOrig() { + return this.orig; + } + + public void setOrig(String orig) { + this.orig = orig; + } + + public String getSourceIP() { + return this.sourceIP; + } + + public void setSourceIP(String sourceIP) { + this.sourceIP = sourceIP; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/IgnoreData.java b/libs/dao/src/main/java/com/akto/dto/IgnoreData.java new file mode 100644 index 0000000000..8abae03979 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/IgnoreData.java @@ -0,0 +1,30 @@ +package com.akto.dto; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.akto.dto.type.SingleTypeInfo.ParamId; + +public class IgnoreData { + Map> ignoredKeysInSelectedAPIs; + Set ignoredKeysInAllAPIs; + public IgnoreData() { + } + public IgnoreData(Map> ignoredKeysInSelectedAPIs, Set ignoredKeysInAllAPIs) { + this.ignoredKeysInSelectedAPIs = ignoredKeysInSelectedAPIs; + this.ignoredKeysInAllAPIs = ignoredKeysInAllAPIs; + } + public Map> getIgnoredKeysInSelectedAPIs() { + return ignoredKeysInSelectedAPIs; + } + public void setIgnoredKeysInSelectedAPIs(Map> ignoredKeysInSelectedAPIs) { + this.ignoredKeysInSelectedAPIs = ignoredKeysInSelectedAPIs; + } + public Set getIgnoredKeysInAllAPIs() { + return ignoredKeysInAllAPIs; + } + public void setIgnoredKeysInAllAPIs(Set ignoredKeysInAllAPIs) { + this.ignoredKeysInAllAPIs = ignoredKeysInAllAPIs; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/KafkaHealthMetric.java b/libs/dao/src/main/java/com/akto/dto/KafkaHealthMetric.java new file mode 100644 index 0000000000..6f228f3816 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/KafkaHealthMetric.java @@ -0,0 +1,91 @@ +package com.akto.dto; + +import org.bson.types.ObjectId; + +import java.util.Objects; + +public class KafkaHealthMetric { + private ObjectId id; + private String topicName; + public static final String TOPIC_NAME = "topicName"; + private int partition; + public static final String PARTITION = "partition"; + private long currentOffset; + private long endOffset; + private long lastUpdated; + + public KafkaHealthMetric(String topicName, int partition, long currentOffset, long endOffset, long lastUpdated) { + this.topicName = topicName; + this.partition = partition; + this.currentOffset = currentOffset; + this.endOffset = endOffset; + this.lastUpdated = lastUpdated; + } + + public KafkaHealthMetric() { + } + + @Override + public int hashCode() { + return Objects.hash(topicName, partition); + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof KafkaHealthMetric)) { + return false; + } + KafkaHealthMetric kafkaHealthMetric = (KafkaHealthMetric) o; + return topicName.equals(kafkaHealthMetric.topicName) && partition == kafkaHealthMetric.partition; + } + + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public String getTopicName() { + return topicName; + } + + public void setTopicName(String topicName) { + this.topicName = topicName; + } + + public int getPartition() { + return partition; + } + + public void setPartition(int partition) { + this.partition = partition; + } + + public long getCurrentOffset() { + return currentOffset; + } + + public void setCurrentOffset(long currentOffset) { + this.currentOffset = currentOffset; + } + + public long getEndOffset() { + return endOffset; + } + + public void setEndOffset(long endOffset) { + this.endOffset = endOffset; + } + + public long getLastUpdated() { + return lastUpdated; + } + + public void setLastUpdated(long lastUpdated) { + this.lastUpdated = lastUpdated; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/Log.java b/libs/dao/src/main/java/com/akto/dto/Log.java new file mode 100644 index 0000000000..4647714991 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/Log.java @@ -0,0 +1,52 @@ +package com.akto.dto; + +public class Log { + private String log; + private String key; + + public static final String TIMESTAMP = "timestamp"; + private int timestamp; + + public Log() { + } + + public Log(String log, String key, int timestamp) { + this.log = log; + this.key = key; + this.timestamp = timestamp; + } + + public String getLog() { + return this.log; + } + + public void setLog(String log) { + this.log = log; + } + + public String getKey() { + return this.key; + } + + public void setKey(String key) { + this.key = key; + } + + public int getTimestamp() { + return this.timestamp; + } + + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + + @Override + public String toString() { + return "{" + + " log='" + getLog() + "'" + + ", key='" + getKey() + "'" + + ", timestamp='" + getTimestamp() + "'" + + "}"; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/Markov.java b/libs/dao/src/main/java/com/akto/dto/Markov.java new file mode 100644 index 0000000000..0925b4878c --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/Markov.java @@ -0,0 +1,131 @@ +package com.akto.dto; + +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class Markov { + private State current; + private State next; + private Map countMap; + Set userIds = new HashSet<>(); + + public Markov(State current, State next, Map countMap, Set userIds) { + this.current = current; + this.next = next; + this.countMap = countMap; + this.userIds = userIds; + } + + public Markov() { + } + + public int getTotalCount() { + int count = 0; + for (String key: countMap.keySet()) { + count += countMap.get(key); + } + return count; + } + + public void increaseCount(String key, String userId) { + int count = countMap.getOrDefault(key, 0); + count += 1; + countMap.put(key, count); + + userIds.add(userId); + } + + public static class State { + private String url; + private String method; + + public State(String url, String method) { + this.url = url; + this.method = method; + } + + public State() {} + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof State)) { + return false; + } + State state = (State) o; + return url.equals(state.url) && method.equals(state.method); + } + + @Override + public int hashCode() { + return Objects.hash(url, method); + } + } + + public State getCurrent() { + return current; + } + + public void setCurrent(State current) { + this.current = current; + } + + public State getNext() { + return next; + } + + public void setNext(State next) { + this.next = next; + } + + public Map getCountMap() { + return countMap; + } + + public void setCountMap(Map countMap) { + this.countMap = countMap; + } + + public Set getUserIds() { + return userIds; + } + + public void setUserIds(Set userIds) { + this.userIds = userIds; + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Markov)) { + return false; + } + Markov state = (Markov) o; + return current.equals(state.current) && next.equals(state.next); + } + + @Override + public int hashCode() { + return Objects.hash(current, next); + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/OTPMessage.java b/libs/dao/src/main/java/com/akto/dto/OTPMessage.java new file mode 100644 index 0000000000..b5f796cc40 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/OTPMessage.java @@ -0,0 +1,51 @@ +package com.akto.dto; + +public class OTPMessage { + + private int id; + private String from; + private String message; + private int timestamp; + + public OTPMessage(int id, String from, String message, int timestamp) { + this.id = id; + this.from = from; + this.message = message; + this.timestamp = timestamp; + } + + public OTPMessage() { } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getTimestamp() { + return timestamp; + } + + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } +} + diff --git a/libs/dao/src/main/java/com/akto/dto/OriginalHttpRequest.java b/libs/dao/src/main/java/com/akto/dto/OriginalHttpRequest.java new file mode 100644 index 0000000000..b43859a4d2 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/OriginalHttpRequest.java @@ -0,0 +1,357 @@ +package com.akto.dto; + +import com.akto.dto.type.RequestTemplate; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.mongodb.BasicDBObject; +import okhttp3.HttpUrl; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; +import java.util.*; + +public class OriginalHttpRequest { + + private static final Gson gson = new Gson(); + private final static ObjectMapper mapper = new ObjectMapper(); + private String url; + private String type; + private String queryParams; + private String method; + private String body; + private Map> headers; + + public OriginalHttpRequest() { } + + // before adding any fields make sure to add them to copy function as wel + public OriginalHttpRequest(String url, String queryParams, String method, String body, Map> headers, String type) { + this.url = url; + this.queryParams = queryParams; + this.method = method; + this.body = body; + this.headers = headers; + this.type = type; + } + + public OriginalHttpRequest copy() { + return new OriginalHttpRequest( + this.url, this.queryParams, this.method, this.body, new HashMap<>(this.headers), this.type + ); + } + + public void buildFromSampleMessage(String message) { + Map json = gson.fromJson(message, Map.class); + + String rawUrl = (String) json.get("path"); + String[] rawUrlArr = rawUrl.split("\\?"); + this.url = rawUrlArr[0]; + if (rawUrlArr.length > 1) { + this.queryParams = rawUrlArr[1]; + } + + this.type = (String) json.get("type"); + + this.method = (String) json.get("method"); + + String requestPayload = (String) json.get("requestPayload"); + this.body = requestPayload.trim(); + + this.headers = buildHeadersMap(json, "requestHeaders"); + } + + public String getJsonRequestBody() { + return rawToJsonString(body, headers); + } + + public static final String FORM_URL_ENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"; + public static final String JSON_CONTENT_TYPE = "application/json"; + + public static String rawToJsonString(String rawRequest, Map> requestHeaders) { + rawRequest = rawRequest.trim(); + String acceptableContentType = getAcceptableContentType(requestHeaders); + if (acceptableContentType != null && rawRequest.length() > 0) { + // only if request payload is of FORM_URL_ENCODED_CONTENT_TYPE we convert it to json + if (acceptableContentType.equals(FORM_URL_ENCODED_CONTENT_TYPE)) { + return convertFormUrlEncodedToJson(rawRequest); + } + } + + return rawRequest; + } + + public boolean isJsonRequest() { + String acceptableContentType = getAcceptableContentType(this.headers); + return acceptableContentType != null && acceptableContentType.equals(JSON_CONTENT_TYPE); + } + + public static String convertFormUrlEncodedToJson(String rawRequest) { + String myStringDecoded = null; + try { + myStringDecoded = URLDecoder.decode(rawRequest, "UTF-8"); + } catch (UnsupportedEncodingException e) { + return rawRequest; + } + String[] parts = myStringDecoded.split("&"); + Map valueMap = new HashMap<>(); + + for(String part: parts){ + String[] keyVal = part.split("="); // The equal separates key and values + if (keyVal.length == 2) { + valueMap.put(keyVal[0], keyVal[1]); + } + } + try { + return mapper.writeValueAsString(valueMap); + } catch (JsonProcessingException e) { + return rawRequest; + } + } + + public static String getAcceptableContentType(Map> headers) { + List acceptableContentTypes = Arrays.asList(JSON_CONTENT_TYPE, FORM_URL_ENCODED_CONTENT_TYPE); + List contentTypeValues = new ArrayList<>(); + for (String k: headers.keySet()) { + if (k.equalsIgnoreCase("content-type")) { + contentTypeValues = headers.get(k); + for (String value: contentTypeValues) { + for (String acceptableContentType: acceptableContentTypes) { + if (value.contains(acceptableContentType)) { + return acceptableContentType; + } + } + } + } + } + return null; + } + + public void buildFromApiSampleMessage(String message) { + BasicDBObject ob = BasicDBObject.parse(message); + BasicDBObject reqObj = (BasicDBObject) ob.get("request"); + Map headersOg = new HashMap<>(); + headersOg = gson.fromJson(reqObj.getString("headers"), headersOg.getClass()); + this.headers = new HashMap<>(); + for (String key: headersOg.keySet()) { + this.headers.put(key, Collections.singletonList(headersOg.get(key))); + } + + this.url = reqObj.getString("url"); + this.queryParams = reqObj.getString("queryParams"); + this.method = reqObj.getString("method"); + this.body = reqObj.getString("body"); + this.type = reqObj.getString("type"); + + } + + public String findHeaderValue(String headerName) { + if (this.headers == null ) return null; + List values = this.headers.get(headerName.trim().toLowerCase()); + if (values == null || values.size() == 0) return null; + return values.get(0); + } + + // queryString2 overrides queryString1 use accordingly + public static String combineQueryParams(String queryString1, String queryString2) { + if (queryString1 == null || queryString1.isEmpty()) return queryString2; + if (queryString2 == null || queryString2.isEmpty()) return queryString1; + + // www.example.com/foo?bar is valid + if (!queryString2.contains("=")) return queryString2; + if (!queryString1.contains("=")) return queryString1; + + String mockUrl1 = "url?" + queryString1; + String mockUrl2 = "url?" + queryString2; + + BasicDBObject queryParamsObject1 = RequestTemplate.getQueryJSON(mockUrl1); + BasicDBObject queryParamsObject2 = RequestTemplate.getQueryJSON(mockUrl2); + + for (String key: queryParamsObject2.keySet()) { + queryParamsObject1.put(key, queryParamsObject2.get(key)); + } + + String json = queryParamsObject1.toJson(); + + return getRawQueryFromJson(json); + } + + public static String getRawQueryFromJson(String requestPayload) { + HttpUrl.Builder builder = new HttpUrl.Builder() + .scheme("https") + .host("www.google.com"); + + BasicDBObject obj = BasicDBObject.parse(requestPayload); + Set keySet = obj.keySet(); + if (keySet.isEmpty()) return null; + + for(String key: keySet) { + Object val = obj.get(key); + builder.addQueryParameter(key, val.toString()); + } + + URI uri = builder.build().uri(); + + return uri.getRawQuery(); + } + + + public static String makeUrlAbsolute(String url, String host, String protocol) throws Exception { + if (host == null) throw new Exception("Host not found"); + if (!url.startsWith("/")) url = "/" + url; + if (host.endsWith("/")) host = host.substring(0, host.length()-1); + + host = host.toLowerCase(); + if (!host.startsWith("http")) { + if (protocol != null) { + host = protocol + "://" + host; + } else { + String firstChar = host.split("")[0]; + try { + Integer.parseInt(firstChar); + host = "http://" + host; + } catch (Exception e) { + host = "https://" + host; + } + } + } + + url = host + url; + + return url; + } + + public String findContentType() { + return findHeaderValue("content-type"); + } + + public String findHostFromHeader() { + return findHeaderValue("host"); + } + + public String findProtocolFromHeader() { + return findHeaderValue("x-forwarded-proto"); + } + + public String getFullUrlWithParams() { + if (this.queryParams == null || this.queryParams.isEmpty()) return this.url; + if (url.contains("?")) return this.url + "&" + this.queryParams; + return this.url + "?" + this.queryParams; + } + + public static Map> buildHeadersMap(Map json, String key) { + return buildHeadersMap((String) json.get(key)); + } + + public static Map> buildHeadersMap(String headersString) { + Map headersFromRequest = gson.fromJson(headersString, Map.class); + Map> headers = new HashMap<>(); + if (headersFromRequest == null) return headers; + for (Object k: headersFromRequest.keySet()) { + List values = headers.getOrDefault(k,new ArrayList<>()); + values.add(headersFromRequest.get(k).toString()); + headers.put(k.toString().toLowerCase(),values); + } + return headers; + } + + public void addHeaderFromLine(String line) { + if (this.headers == null || this.headers.isEmpty()) { + this.headers = new HashMap<>(); + } + + int separator = line.indexOf(":"); + if (separator < 0 || separator > line.length()-2) { + return; + } + String headerKey = line.substring(0, separator); + List headerValues = this.headers.get(headerKey); + if (headerValues == null) { + headerValues = new ArrayList<>(); + this.headers.put(headerKey, headerValues); + } + + String headerValue = line.substring(separator+2); + + headerValues.add(headerValue); + } + + public void appendToPayload(String line) { + if (this.body == null) { + this.body = ""; + } + + this.body += line; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public Map> getHeaders() { + return headers; + } + + public void setHeaders(Map> headers) { + this.headers = headers; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getQueryParams() { + return queryParams; + } + + public void setQueryParams(String queryParams) { + this.queryParams = queryParams; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public boolean setMethodAndQP(String line) { + String[] tokens = line.split(" "); + if (tokens.length != 3) { + return false; + } + + this.method = tokens[0]; + String fullUrl = tokens[1]; + + int qpIndex = fullUrl.indexOf("?"); + if (qpIndex > -1 && qpIndex <= fullUrl.length()) { + this.url = fullUrl.substring(0, qpIndex); + this.queryParams = fullUrl.substring(qpIndex+1); + } else { + this.url = fullUrl; + this.queryParams = ""; + } + + this.type = tokens[2]; + + return true; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/OriginalHttpResponse.java b/libs/dao/src/main/java/com/akto/dto/OriginalHttpResponse.java new file mode 100644 index 0000000000..7b04c0e679 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/OriginalHttpResponse.java @@ -0,0 +1,108 @@ +package com.akto.dto; + +import com.google.gson.Gson; + +import org.apache.commons.lang3.math.NumberUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class OriginalHttpResponse { + + private static final Gson gson = new Gson(); + private String body; + private Map> headers; + private int statusCode; + + public OriginalHttpResponse() { } + + // before adding any fields make sure to add them to copy function as wel + public OriginalHttpResponse(String body, Map> headers, int statusCode) { + this.body = body; + this.headers = headers; + this.statusCode = statusCode; + } + + public OriginalHttpResponse copy() { + return new OriginalHttpResponse(this.body, new HashMap<>(this.headers), this.statusCode); + } + + public void buildFromSampleMessage(String message) { + Map json = gson.fromJson(message, Map.class); + + String requestPayload = (String) json.get("responsePayload"); + this.body = requestPayload.trim(); + this.headers = OriginalHttpRequest.buildHeadersMap(json, "responseHeaders"); + this.statusCode = Integer.parseInt(json.get("statusCode").toString()); + } + + public void addHeaderFromLine(String line) { + if (this.headers == null || this.headers.isEmpty()) { + this.headers = new HashMap<>(); + } + + int separator = line.indexOf(":"); + if (separator < 0 || separator > line.length()-2) { + return; + } + String headerKey = line.substring(0, separator); + List headerValues = this.headers.get(headerKey); + if (headerValues == null) { + headerValues = new ArrayList<>(); + this.headers.put(headerKey, headerValues); + } + + String headerValue = line.substring(separator+2); + + headerValues.add(headerValue); + } + + public void appendToPayload(String line) { + if (this.body == null) { + this.body = ""; + } + + this.body += line; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public Map> getHeaders() { + return headers; + } + + public void setHeaders(Map> headers) { + this.headers = headers; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public boolean setStatusFromLine(String line) { + String[] tokens = line.split(" "); + if (tokens.length < 3) { + return false; + } + + String statusStr = tokens[1]; + if (NumberUtils.isDigits(statusStr)) { + this.statusCode = Integer.parseInt(statusStr); + return true; + } else { + return false; + } + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/PendingInviteCode.java b/libs/dao/src/main/java/com/akto/dto/PendingInviteCode.java new file mode 100644 index 0000000000..b60e4ce0d4 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/PendingInviteCode.java @@ -0,0 +1,63 @@ +package com.akto.dto; + +import org.bson.types.ObjectId; + +public class PendingInviteCode { + + private ObjectId id; + private String inviteCode; + public static final String INVITE_CODE = "inviteCode"; + private int issuer; + private String inviteeEmailId; + private long expiry; + + public PendingInviteCode() { + } + + public PendingInviteCode(String inviteCode, int issuer, String inviteeEmailId, long expiry) { + this.inviteCode = inviteCode; + this.issuer = issuer; + this.inviteeEmailId = inviteeEmailId; + this.expiry = expiry; + } + + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public String getInviteCode() { + return inviteCode; + } + + public void setInviteCode(String inviteCode) { + this.inviteCode = inviteCode; + } + + public int getIssuer() { + return issuer; + } + + public void setIssuer(int issuer) { + this.issuer = issuer; + } + + public String getInviteeEmailId() { + return inviteeEmailId; + } + + public void setInviteeEmailId(String inviteeEmailId) { + this.inviteeEmailId = inviteeEmailId; + } + + public long getExpiry() { + return expiry; + } + + public void setExpiry(long expiry) { + this.expiry = expiry; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/PercentageMatchRequest.java b/libs/dao/src/main/java/com/akto/dto/PercentageMatchRequest.java new file mode 100644 index 0000000000..ef2576fc1e --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/PercentageMatchRequest.java @@ -0,0 +1,33 @@ +package com.akto.dto; + +import java.util.Map; + +public class PercentageMatchRequest { + + private OriginalHttpResponse response; + private Map excludedKeys; + + public PercentageMatchRequest(OriginalHttpResponse response, Map excludedKeys) { + this.response = response; + this.excludedKeys = excludedKeys; + } + + public PercentageMatchRequest() { } + + public OriginalHttpResponse getResponse() { + return response; + } + + public void setResponse(OriginalHttpResponse response) { + this.response = response; + } + + public Map getExcludedKeys() { + return excludedKeys; + } + + public void setExcludedKeys(Map excludedKeys) { + this.excludedKeys = excludedKeys; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/PolicyCatalog.java b/libs/dao/src/main/java/com/akto/dto/PolicyCatalog.java new file mode 100644 index 0000000000..f01044604d --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/PolicyCatalog.java @@ -0,0 +1,31 @@ +package com.akto.dto; + +import java.util.Map; + +public class PolicyCatalog { + private ApiInfo apiInfo; + private Map filterSampleDataMap; + + public PolicyCatalog() {} + + public PolicyCatalog(ApiInfo apiInfo, Map filterSampleDataMap) { + this.apiInfo = apiInfo; + this.filterSampleDataMap = filterSampleDataMap; + } + + public ApiInfo getApiInfo() { + return apiInfo; + } + + public void setApiInfo(ApiInfo apiInfo) { + this.apiInfo = apiInfo; + } + + public Map getFilterSampleDataMap() { + return filterSampleDataMap; + } + + public void setFilterSampleDataMap(Map filterSampleDataMap) { + this.filterSampleDataMap = filterSampleDataMap; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/RBAC.java b/libs/dao/src/main/java/com/akto/dto/RBAC.java new file mode 100644 index 0000000000..023c0c2cb0 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/RBAC.java @@ -0,0 +1,49 @@ +package com.akto.dto; + + +import org.bson.types.ObjectId; + +public class RBAC { + + private ObjectId id; + private int userId; + public static final String USER_ID = "userId"; + private Role role; + public static final String ROLE = "role"; + + public enum Role { + ADMIN, MEMBER + } + + public RBAC(int userId, Role role) { + this.userId = userId; + this.role = role; + } + + public RBAC() { + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public Role getRole() { + return role; + } + + public void setRole(Role role) { + this.role = role; + } + + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/RawApi.java b/libs/dao/src/main/java/com/akto/dto/RawApi.java new file mode 100644 index 0000000000..ca5d2b58bb --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/RawApi.java @@ -0,0 +1,54 @@ +package com.akto.dto; + +public class RawApi { + + private OriginalHttpRequest request; + private OriginalHttpResponse response; + private String originalMessage; + + public RawApi(OriginalHttpRequest request, OriginalHttpResponse response, String originalMessage) { + this.request = request; + this.response = response; + this.originalMessage = originalMessage; + } + + public static RawApi buildFromMessage(String message) { + OriginalHttpRequest request = new OriginalHttpRequest(); + request.buildFromSampleMessage(message); + + OriginalHttpResponse response = new OriginalHttpResponse(); + response.buildFromSampleMessage(message); + + return new RawApi(request, response, message); + } + + public RawApi copy() { + return new RawApi(this.request.copy(), this.response.copy(), this.originalMessage); + } + + public RawApi() { } + + public OriginalHttpRequest getRequest() { + return request; + } + + public void setRequest(OriginalHttpRequest request) { + this.request = request; + } + + public OriginalHttpResponse getResponse() { + return response; + } + + public void setResponse(OriginalHttpResponse response) { + this.response = response; + } + + public String getOriginalMessage() { + return originalMessage; + } + + public void setOriginalMessage(String originalMessage) { + this.originalMessage = originalMessage; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/RecordedLoginFlowInput.java b/libs/dao/src/main/java/com/akto/dto/RecordedLoginFlowInput.java new file mode 100644 index 0000000000..088cf2b4cd --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/RecordedLoginFlowInput.java @@ -0,0 +1,51 @@ +package com.akto.dto; + +public class RecordedLoginFlowInput { + + private String content; + private String tokenFetchCommand; + private String outputFilePath; + private String errorFilePath; + + public RecordedLoginFlowInput(String content, String tokenFetchCommand, String outputFilePath, String errorFilePath) { + this.content = content; + this.tokenFetchCommand = tokenFetchCommand; + this.outputFilePath = outputFilePath; + this.errorFilePath = errorFilePath; + } + + public RecordedLoginFlowInput() { } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getTokenFetchCommand() { + return tokenFetchCommand; + } + + public void setTokenFetchCommand(String tokenFetchCommand) { + this.tokenFetchCommand = tokenFetchCommand; + } + + public String getOutputFilePath() { + return outputFilePath; + } + + public void setOutputFilePath(String outputFilePath) { + this.outputFilePath = outputFilePath; + } + + public String getErrorFilePath() { + return errorFilePath; + } + + public void setErrorFilePath(String errorFilePath) { + this.errorFilePath = errorFilePath; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/Relationship.java b/libs/dao/src/main/java/com/akto/dto/Relationship.java new file mode 100644 index 0000000000..6ff4625354 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/Relationship.java @@ -0,0 +1,150 @@ +package com.akto.dto; + + +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class Relationship { + private ApiRelationInfo parent; + private ApiRelationInfo child; + private Set userIds = new HashSet<>(); + private Map countMap; + + public Relationship(ApiRelationInfo parent, ApiRelationInfo child, Set userIds, Map countMap) { + this.parent = parent; + this.child = child; + this.userIds = userIds; + this.countMap = countMap; + } + + public Relationship() {} + + public static class ApiRelationInfo { + private String url; + private String method; + private boolean isHeader; + private String param; + private int responseCode; + + public ApiRelationInfo() { + } + + public ApiRelationInfo(String url, String method, boolean isHeader, String param, int responseCode) { + this.url = url; + this.method = method; + this.isHeader = isHeader; + this.param = param; + this.responseCode = responseCode; + } + + @Override + public int hashCode() { + return Objects.hash(url, method, responseCode, isHeader, param); + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof ApiRelationInfo)) { + return false; + } + ApiRelationInfo apiRelationInfo = (ApiRelationInfo) o; + return url.equals(apiRelationInfo.url) && + method.equals(apiRelationInfo.method) && + responseCode ==apiRelationInfo.responseCode && + isHeader ==apiRelationInfo.isHeader && + param.equals(apiRelationInfo.param); + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public boolean isHeader() { + return isHeader; + } + + public void setHeader(boolean header) { + isHeader = header; + } + + public String getParam() { + return param; + } + + public void setParam(String param) { + this.param = param; + } + + public int getResponseCode() { + return responseCode; + } + + public void setResponseCode(int responseCode) { + this.responseCode = responseCode; + } + } + + @Override + public int hashCode() { + return Objects.hash(parent,child); + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Relationship)) { + return false; + } + Relationship relationship = (Relationship) o; + return parent.equals(relationship.parent) && child.equals(relationship.child); + } + + public ApiRelationInfo getParent() { + return parent; + } + + public void setParent(ApiRelationInfo parent) { + this.parent = parent; + } + + public ApiRelationInfo getChild() { + return child; + } + + public void setChild(ApiRelationInfo child) { + this.child = child; + } + + public Set getUserIds() { + return userIds; + } + + public void setUserIds(Set userIds) { + this.userIds = userIds; + } + + public Map getCountMap() { + return countMap; + } + + public void setCountMap(Map countMap) { + this.countMap = countMap; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/SampleRequestReplayResponse.java b/libs/dao/src/main/java/com/akto/dto/SampleRequestReplayResponse.java new file mode 100644 index 0000000000..c819182cd4 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/SampleRequestReplayResponse.java @@ -0,0 +1,35 @@ +package com.akto.dto; + +import java.util.ArrayList; +import java.util.Map; +import java.util.Set; + +public class SampleRequestReplayResponse { + + private ArrayList replayedResponses; + private ArrayList>> replayedResponseMap; + + public SampleRequestReplayResponse(ArrayList replayedResponses, ArrayList>> replayedResponseMap) { + this.replayedResponses = replayedResponses; + this.replayedResponseMap = replayedResponseMap; + } + + public SampleRequestReplayResponse() { } + + public ArrayList getReplayedResponses() { + return replayedResponses; + } + + public void setReplayedResponses(ArrayList replayedResponses) { + this.replayedResponses = replayedResponses; + } + + public ArrayList>> getReplayedResponseMap() { + return replayedResponseMap; + } + + public void setReplayedResponseMap(ArrayList>> replayedResponseMap) { + this.replayedResponseMap = replayedResponseMap; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/Scan.java b/libs/dao/src/main/java/com/akto/dto/Scan.java new file mode 100644 index 0000000000..0fe42351e8 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/Scan.java @@ -0,0 +1,80 @@ +package com.akto.dto; + +import org.bson.codecs.pojo.annotations.BsonId; + +import java.util.List; + +public class Scan { + + @BsonId + private int id; + + private int testEvnSettingsId; + + private int startTimestamp; + private int endTimestamp; + + private List attempts; + + public Scan() { + } + + public Scan(int id, int testEvnSettingsId, int startTimestamp, int endTimestamp, List attempts) { + this.id = id; + this.testEvnSettingsId = testEvnSettingsId; + this.startTimestamp = startTimestamp; + this.endTimestamp = endTimestamp; + this.attempts = attempts; + } + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + public int getTestEvnSettingsId() { + return this.testEvnSettingsId; + } + + public void setTestEvnSettingsId(int testEvnSettingsId) { + this.testEvnSettingsId = testEvnSettingsId; + } + + public int getStartTimestamp() { + return this.startTimestamp; + } + + public void setStartTimestamp(int startTimestamp) { + this.startTimestamp = startTimestamp; + } + + public int getEndTimestamp() { + return this.endTimestamp; + } + + public void setEndTimestamp(int endTimestamp) { + this.endTimestamp = endTimestamp; + } + + public List getAttempts() { + return this.attempts; + } + + public void setAttempts(List attempts) { + this.attempts = attempts; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", testEvnSettingsId='" + getTestEvnSettingsId() + "'" + + ", startTimestamp='" + getStartTimestamp() + "'" + + ", endTimestamp='" + getEndTimestamp() + "'" + + ", attempts='" + getAttempts() + "'" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/SensitiveParamInfo.java b/libs/dao/src/main/java/com/akto/dto/SensitiveParamInfo.java new file mode 100644 index 0000000000..a3c4e58727 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/SensitiveParamInfo.java @@ -0,0 +1,130 @@ +package com.akto.dto; + +import java.util.Objects; + +public class SensitiveParamInfo { + private String url; + private String method; + private int responseCode; + private boolean isHeader; + private String param; + private int apiCollectionId; + public static final String SENSITIVE = "sensitive"; + private boolean sensitive; + public static final String SAMPLE_DATA_SAVED = "sampleDataSaved"; + private boolean sampleDataSaved; + + + public SensitiveParamInfo() { + } + + public SensitiveParamInfo(String url, String method, int responseCode, boolean isHeader, String param, int apiCollectionId, boolean sensitive) { + this.url = url; + this.method = method; + this.responseCode = responseCode; + this.isHeader = isHeader; + this.param = param; + this.apiCollectionId = apiCollectionId; + this.sensitive = sensitive; + this.sampleDataSaved = false; + } + + public String getUrl() { + return this.url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getMethod() { + return this.method; + } + + public void setMethod(String method) { + this.method = method; + } + + public int getResponseCode() { + return this.responseCode; + } + + public void setResponseCode(int responseCode) { + this.responseCode = responseCode; + } + + public boolean isIsHeader() { + return this.isHeader; + } + + public boolean getIsHeader() { + return this.isHeader; + } + + public void setIsHeader(boolean isHeader) { + this.isHeader = isHeader; + } + + public String getParam() { + return this.param; + } + + public void setParam(String param) { + this.param = param; + } + + public int getApiCollectionId() { + return this.apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public boolean isSensitive() { + return this.sensitive; + } + + public boolean getSensitive() { + return this.sensitive; + } + + public void setSensitive(boolean sensitive) { + this.sensitive = sensitive; + } + + @Override + public String toString() { + return "{" + + " url='" + getUrl() + "'" + + ", method='" + getMethod() + "'" + + ", responseCode='" + getResponseCode() + "'" + + ", isHeader='" + isIsHeader() + "'" + + ", param='" + getParam() + "'" + + ", apiCollectionId='" + getApiCollectionId() + "'" + + ", sensitive='" + isSensitive() + "'" + + "}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SensitiveParamInfo that = (SensitiveParamInfo) o; + return responseCode == that.responseCode && isHeader == that.isHeader && apiCollectionId == that.apiCollectionId && url.equals(that.url) && method.equals(that.method) && param.equals(that.param); + } + + @Override + public int hashCode() { + return Objects.hash(url, method, responseCode, isHeader, param, apiCollectionId); + } + + public boolean isSampleDataSaved() { + return sampleDataSaved; + } + + public void setSampleDataSaved(boolean sampleDataSaved) { + this.sampleDataSaved = sampleDataSaved; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/SensitiveSampleData.java b/libs/dao/src/main/java/com/akto/dto/SensitiveSampleData.java new file mode 100644 index 0000000000..6bce2f95d4 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/SensitiveSampleData.java @@ -0,0 +1,35 @@ +package com.akto.dto; + +import com.akto.dto.type.SingleTypeInfo; + +import java.util.List; + +public class SensitiveSampleData { + private SingleTypeInfo.ParamId id; + public static final String SAMPLE_DATA = "sampleData"; + private List sampleData; + public static final int cap = 10; + + public SensitiveSampleData() {} + + public SensitiveSampleData(SingleTypeInfo.ParamId id, List sampleData) { + this.id = id; + this.sampleData = sampleData; + } + + public SingleTypeInfo.ParamId getId() { + return id; + } + + public void setId(SingleTypeInfo.ParamId id) { + this.id = id; + } + + public List getSampleData() { + return sampleData; + } + + public void setSampleData(List sampleData) { + this.sampleData = sampleData; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/SignupInfo.java b/libs/dao/src/main/java/com/akto/dto/SignupInfo.java new file mode 100644 index 0000000000..5eaf972e92 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/SignupInfo.java @@ -0,0 +1,337 @@ +package com.akto.dto; + +import com.mongodb.BasicDBObject; +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +@BsonDiscriminator +public abstract class SignupInfo { + String key; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public Config.ConfigType getConfigType() { + return configType; + } + + public void setConfigType(Config.ConfigType configType) { + this.configType = configType; + } + + public int getTimestamp() { + return timestamp; + } + + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + + Config.ConfigType configType; + int timestamp = (int) (System.currentTimeMillis()/1000l); + + @BsonDiscriminator + public static class GoogleSignupInfo extends SignupInfo { + long expiresInSeconds; + + public GoogleSignupInfo() {} + + String accessToken; + String refreshToken; + + public GoogleSignupInfo(String key, String accessToken, String refreshToken, long expiresInSeconds) { + this.key = key; + this.configType = Config.ConfigType.GOOGLE; + this.expiresInSeconds = expiresInSeconds; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + public long getExpiresInSeconds() { + return expiresInSeconds; + } + + public void setExpiresInSeconds(long expiresInSeconds) { + this.expiresInSeconds = expiresInSeconds; + } + } + + @BsonDiscriminator + public static class SlackSignupInfo extends SignupInfo { + + public SlackSignupInfo() {} + + public SlackSignupInfo(String key, boolean ok, String appId, String authedUserAccessToken, String authedUserId, String authedUserScope, + String authedUserTokenType, String scope, String tokenType, String accessToken, String botUserId, + String teamName, String teamId, String enterpriseName, String enterpriseId, boolean isEnterpriseInstall) { + this.key = key; + this.ok = ok; + this.appId = appId; + this.authedUserAccessToken = authedUserAccessToken; + this.authedUserId = authedUserId; + this.authedUserScope = authedUserScope; + this.authedUserTokenType = authedUserTokenType; + this.scope = scope; + this.tokenType = tokenType; + this.accessToken = accessToken; + this.botUserId = botUserId; + this.teamName = teamName; + this.teamId = teamId; + this.enterpriseName = enterpriseName; + this.enterpriseId = enterpriseId; + this.isEnterpriseInstall = isEnterpriseInstall; + this.configType = Config.ConfigType.SLACK; + } + + private boolean ok; + private String appId; + private String authedUserAccessToken; + private String authedUserId; + private String authedUserScope; + private String authedUserTokenType; + private String scope; + private String tokenType; + private String accessToken; + private String botUserId; + private String teamName; + private String teamId; + private String enterpriseName; + private String enterpriseId; + private boolean isEnterpriseInstall; + + public boolean isOk() { + return ok; + } + + public void setOk(boolean ok) { + this.ok = ok; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getAuthedUserAccessToken() { + return authedUserAccessToken; + } + + public void setAuthedUserAccessToken(String authedUserAccessToken) { + this.authedUserAccessToken = authedUserAccessToken; + } + + public String getAuthedUserId() { + return authedUserId; + } + + public void setAuthedUserId(String authedUserId) { + this.authedUserId = authedUserId; + } + + public String getAuthedUserScope() { + return authedUserScope; + } + + public void setAuthedUserScope(String authedUserScope) { + this.authedUserScope = authedUserScope; + } + + public String getAuthedUserTokenType() { + return authedUserTokenType; + } + + public void setAuthedUserTokenType(String authedUserTokenType) { + this.authedUserTokenType = authedUserTokenType; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public String getTokenType() { + return tokenType; + } + + public void setTokenType(String tokenType) { + this.tokenType = tokenType; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getBotUserId() { + return botUserId; + } + + public void setBotUserId(String botUserId) { + this.botUserId = botUserId; + } + + public String getTeamName() { + return teamName; + } + + public void setTeamName(String teamName) { + this.teamName = teamName; + } + + public String getTeamId() { + return teamId; + } + + public void setTeamId(String teamId) { + this.teamId = teamId; + } + + public String getEnterpriseName() { + return enterpriseName; + } + + public void setEnterpriseName(String enterpriseName) { + this.enterpriseName = enterpriseName; + } + + public String getEnterpriseId() { + return enterpriseId; + } + + public void setEnterpriseId(String enterpriseId) { + this.enterpriseId = enterpriseId; + } + + public boolean isEnterpriseInstall() { + return isEnterpriseInstall; + } + + public void setEnterpriseInstall(boolean enterpriseInstall) { + isEnterpriseInstall = enterpriseInstall; + } + } + + @BsonDiscriminator + public static class WebpushSubscriptionInfo extends SignupInfo { + + public WebpushSubscriptionInfo() {} + + public WebpushSubscriptionInfo(BasicDBObject obj) { + this.endpoint = obj.getString("endpoint"); + HashMap keys = (HashMap) obj.get("keys"); + this.auth = keys.get("auth"); + this.authKey = keys.get("p256dh"); + this.configType = Config.ConfigType.WEBPUSH; + this.key = configType+"-ankush"; + } + + + private String auth; + private String authKey; + private String endpoint; + + public String getAuthKey() { + return authKey; + } + + public void setAuthKey(String authKey) { + this.authKey = authKey; + } + + public void setAuth(String auth) { + this.auth = auth; + } + + public String getAuth() { + return auth; + } + + /** + * Returns the base64 encoded auth string as a byte[] + */ + public byte[] authAsBytes() { + return Base64.getUrlDecoder().decode(getAuth()); + } + + /** + * Returns the base64 encoded public key string as a byte[] + */ + public byte[] keyAsBytes() { + return Base64.getUrlDecoder().decode(getAuthKey()); + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getEndpoint() { + return endpoint; + } + } + + @BsonDiscriminator + public static class PasswordHashInfo extends SignupInfo { + + String passhash, salt; + + public PasswordHashInfo() {} + + public PasswordHashInfo(String passhash, String salt) { + this.passhash = passhash; + this.salt = salt; + this.configType = Config.ConfigType.PASSWORD; + this.key = configType+"-ankush"; + } + + public String getPasshash() { + return passhash; + } + + public void setPasshash(String passhash) { + this.passhash = passhash; + } + + public String getSalt() { + return salt; + } + + public void setSalt(String salt) { + this.salt = salt; + } + + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/SignupUserInfo.java b/libs/dao/src/main/java/com/akto/dto/SignupUserInfo.java new file mode 100644 index 0000000000..60cdc9dfc6 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/SignupUserInfo.java @@ -0,0 +1,68 @@ +package com.akto.dto; + +import java.util.List; + +public class SignupUserInfo { + + User user; + + public SignupUserInfo() {} + + String companyName, teamName; + boolean completedSignup = false; + int formVersion = 1; + + public SignupUserInfo(User user, String companyName, String teamName, List metrics, List emailInvitations) { + this.user = user; + this.companyName = companyName; + this.teamName = teamName; + this.metrics = metrics; + this.emailInvitations = emailInvitations; + } + + List metrics; + + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public String getCompanyName() { + return companyName; + } + + public void setCompanyName(String companyName) { + this.companyName = companyName; + } + + public String getTeamName() { + return teamName; + } + + public void setTeamName(String teamName) { + this.teamName = teamName; + } + + public List getMetrics() { + return metrics; + } + + public void setMetrics(List metrics) { + this.metrics = metrics; + } + + public List getEmailInvitations() { + return emailInvitations; + } + + public void setEmailInvitations(List emailInvitations) { + this.emailInvitations = emailInvitations; + } + + List emailInvitations; + +} diff --git a/libs/dao/src/main/java/com/akto/dto/TagConfig.java b/libs/dao/src/main/java/com/akto/dto/TagConfig.java new file mode 100644 index 0000000000..c40d515daa --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/TagConfig.java @@ -0,0 +1,101 @@ +package com.akto.dto; + +import com.akto.dao.context.Context; +import com.akto.dto.data_types.Conditions; + +import org.bson.types.ObjectId; + +public class TagConfig { + private ObjectId id; + + public static final String NAME = "name"; + private String name; + + private int creatorId; + + public static final String TIMESTAMP = "timestamp"; + private int timestamp; + + public static final String ACTIVE = "active"; + private boolean active; + + public static final String KEY_CONDITIONS = "keyConditions"; + Conditions keyConditions; + + + public TagConfig() { + } + + public TagConfig(String name, int creatorId, boolean active, Conditions keyConditions) { + this.name = name; + this.creatorId = creatorId; + this.timestamp = Context.now(); + this.active = active; + this.keyConditions = keyConditions; + } + + public ObjectId getId() { + return this.id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public int getCreatorId() { + return this.creatorId; + } + + public void setCreatorId(int creatorId) { + this.creatorId = creatorId; + } + + public int getTimestamp() { + return this.timestamp; + } + + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + + public boolean isActive() { + return this.active; + } + + public boolean getActive() { + return this.active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public Conditions getKeyConditions() { + return this.keyConditions; + } + + public void setKeyConditions(Conditions keyConditions) { + this.keyConditions = keyConditions; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", name='" + getName() + "'" + + ", creatorId='" + getCreatorId() + "'" + + ", timestamp='" + getTimestamp() + "'" + + ", active='" + isActive() + "'" + + ", keyConditions='" + getKeyConditions() + "'" + + "}"; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/Team.java b/libs/dao/src/main/java/com/akto/dto/Team.java new file mode 100644 index 0000000000..9e373ab7dd --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/Team.java @@ -0,0 +1,58 @@ +package com.akto.dto; + +import com.akto.dao.context.Context; +import org.bson.codecs.pojo.annotations.BsonId; + +public class Team { + + public enum UserType { + MEMBER, VISITOR, GUEST + } + + @BsonId + int id; + String teamName; + int creationTs; + int ownerId; + + public Team() {} + + public Team(int id, String teamName, int ownerId) { + this.id = id; + this.teamName = teamName; + this.creationTs = Context.now(); + this.ownerId = ownerId; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getTeamName() { + return teamName; + } + + public void setTeamName(String teamName) { + this.teamName = teamName; + } + + public int getCreationTs() { + return creationTs; + } + + public void setCreationTs(int creationTs) { + this.creationTs = creationTs; + } + + public int getOwnerId() { + return ownerId; + } + + public void setOwnerId(int ownerId) { + this.ownerId = ownerId; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/TestEnvSettings.java b/libs/dao/src/main/java/com/akto/dto/TestEnvSettings.java new file mode 100644 index 0000000000..7ed70cd3eb --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/TestEnvSettings.java @@ -0,0 +1,83 @@ +package com.akto.dto; + +import java.util.Map; + +import com.akto.dao.context.Context; +import com.akto.dto.auth.APIAuth; + +import org.bson.codecs.pojo.annotations.BsonId; + +public class TestEnvSettings { + + public enum LogLevel { + DEBUG, INFO, WARN, ERROR + } + + @BsonId + int id; + + LogLevel logLevel; + Map authProtocolSettings; + int maxRequestsPerSec; + int maxResponseTime; + int contentId; + + public TestEnvSettings() {} + + public TestEnvSettings(LogLevel logLevel, Map authProtocolSettings, int maxRequestsPerSec, int maxResponseTime, int contentId) { + this.id = Context.now(); + this.logLevel = logLevel; + this.authProtocolSettings = authProtocolSettings; + this.maxRequestsPerSec = maxRequestsPerSec; + this.maxResponseTime = maxResponseTime; + this.contentId = contentId; + } + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + public LogLevel getLogLevel() { + return this.logLevel; + } + + public void setLogLevel(LogLevel logLevel) { + this.logLevel = logLevel; + } + + public Map getAuthProtocolSettings() { + return this.authProtocolSettings; + } + + public void setAuthProtocolSettings(Map authProtocolSettings) { + this.authProtocolSettings = authProtocolSettings; + } + + public int getMaxRequestsPerSec() { + return this.maxRequestsPerSec; + } + + public void setMaxRequestsPerSec(int maxRequestsPerSec) { + this.maxRequestsPerSec = maxRequestsPerSec; + } + + public int getMaxResponseTime() { + return this.maxResponseTime; + } + + public void setMaxResponseTime(int maxResponseTime) { + this.maxResponseTime = maxResponseTime; + } + + public int getContentId() { + return this.contentId; + } + + public void setContentId(int contentId) { + this.contentId = contentId; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/TestRun.java b/libs/dao/src/main/java/com/akto/dto/TestRun.java new file mode 100644 index 0000000000..7fef002260 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/TestRun.java @@ -0,0 +1,84 @@ +package com.akto.dto; + +import org.bson.codecs.pojo.annotations.BsonId; + +import java.util.List; + +import com.akto.dao.context.Context; + +public class TestRun { + + public static enum TestRunStatus { + INIT, STARTED, COMPLETED, FAILED; + } + + @BsonId + int id; + int apiSpecId; + int testEnvSettingsId; + String testRunStatus; + List attempts; + + public TestRun() { + } + + public TestRun(int apiSpecId, int testEnvSettingsId, List attempts) { + this.id = Context.now(); + this.apiSpecId = apiSpecId; + this.testEnvSettingsId = testEnvSettingsId; + this.attempts = attempts; + this.testRunStatus = TestRunStatus.INIT.toString(); + } + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + public int getApiSpecId() { + return this.apiSpecId; + } + + public void setApiSpecId(int apiSpecId) { + this.apiSpecId = apiSpecId; + } + + public int getTestEnvSettingsId() { + return this.testEnvSettingsId; + } + + public void setTestEnvSettingsId(int testEnvSettingsId) { + this.testEnvSettingsId = testEnvSettingsId; + } + + public List getAttempts() { + return this.attempts; + } + + public void setAttempts(List attempts) { + this.attempts = attempts; + } + + public String getTestRunStatus() { + return this.testRunStatus; + } + + public void setTestRunStatus(String testRunStatus) { + this.testRunStatus = testRunStatus; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", apiSpecId='" + getApiSpecId() + "'" + + ", testEnvSettingsId='" + getTestEnvSettingsId() + "'" + + ", attemps='" + getAttempts() + "'" + + ", testRunStatus='" + getTestRunStatus() + "'" + + "}"; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/User.java b/libs/dao/src/main/java/com/akto/dto/User.java new file mode 100644 index 0000000000..ff3cccbd58 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/User.java @@ -0,0 +1,109 @@ +package com.akto.dto; + +import com.akto.dao.context.Context; +import com.mongodb.BasicDBObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class User { + private String name; + private String login; + private int id; + private List refreshTokens; + public static final String LAST_LOGIN_TS = "lastLoginTs"; + private int lastLoginTs; + + private Map accounts; + + private Map signupInfoMap; + + public User() {} + + public User(String name, String login, Map accounts, Map signupInfoMap) { + this.name = name; + this.login = login; + this.id = Context.getId(); + this.accounts = accounts; + this.signupInfoMap = signupInfoMap; + this.refreshTokens = new ArrayList<>(); + } + + public static User create(String name, String login, SignupInfo info, Map accountEntryMap) { + Map infoMap = new HashMap<>(); + infoMap.put(info.getKey(), info); + return new User(name, login, accountEntryMap, infoMap); + } + + public static BasicDBObject convertUserToUserDetails(User user) { + BasicDBObject userDetails = new BasicDBObject(); + userDetails.put("id",user.getId()); + userDetails.put("login",user.getLogin()); + userDetails.put("name",user.getName()); + + return userDetails; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + @Override + public String toString() { + return String.format("User (name=%s, login=%s, id=%s)", name, login, id+""); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Map getAccounts() { + return accounts; + } + + public void setAccounts(Map accounts) { + this.accounts = accounts; + } + + public Map getSignupInfoMap() { + return signupInfoMap; + } + + public void setSignupInfoMap(Map signupInfoMap) { + this.signupInfoMap = signupInfoMap; + } + + public List getRefreshTokens() { + return refreshTokens; + } + + public void setRefreshTokens(List refreshTokens) { + this.refreshTokens = refreshTokens; + } + + public int getLastLoginTs() { + return lastLoginTs; + } + + public void setLastLoginTs(int lastLoginTs) { + this.lastLoginTs = lastLoginTs; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/UserAccountEntry.java b/libs/dao/src/main/java/com/akto/dto/UserAccountEntry.java new file mode 100644 index 0000000000..3de58861f5 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/UserAccountEntry.java @@ -0,0 +1,37 @@ +package com.akto.dto; + +public class UserAccountEntry { + private int accountId; + private String name; + private boolean isDefault = false; + + public UserAccountEntry() {} + + public UserAccountEntry(int accountId) { + this.accountId = accountId; + } + + public int getAccountId() { + return accountId; + } + + public void setAccountId(int accountId) { + this.accountId = accountId; + } + + public boolean isDefault() { + return isDefault; + } + + public void setDefault(boolean aDefault) { + isDefault = aDefault; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/api_workflow/Graph.java b/libs/dao/src/main/java/com/akto/dto/api_workflow/Graph.java new file mode 100644 index 0000000000..7502aa9f18 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/api_workflow/Graph.java @@ -0,0 +1,84 @@ +package com.akto.dto.api_workflow; + +import com.akto.dto.testing.WorkflowNodeDetails; +import com.akto.dto.testing.WorkflowTest; +import com.mongodb.BasicDBObject; + +import java.util.*; + +public class Graph { + private Map nodes = new HashMap<>(); + + public Graph() { } + + public void buildGraph(WorkflowTest workflowTest) { + List edges = workflowTest.getEdges(); + Map mapNodeIdToWorkflowNodeDetails = workflowTest.getMapNodeIdToWorkflowNodeDetails(); + + for (String edge: edges) { + BasicDBObject basicDBObject = BasicDBObject.parse(edge); + String sourceId = basicDBObject.getString("source"); + String targetId = basicDBObject.getString("target"); + if (sourceId == null || targetId == null) continue; + + // Skip if START node + if (sourceId.equals("1")) continue; + + Node node = getNode(sourceId); + if (node == null) { + WorkflowNodeDetails workflowNodeDetails = mapNodeIdToWorkflowNodeDetails.get(sourceId); + node = new Node(sourceId, workflowNodeDetails); + } + node.addNeighbours(targetId); + addNode(node); + } + + } + + public List sort() { + List result = new ArrayList<>(); + Set visited = new HashSet<>(); + + for (Node node: nodes.values()) { + if (visited.contains(node.getId())) continue; + rec(node, visited, result); + } + + Collections.reverse(result); + return result; + } + + private void rec(Node node, Set visited, List result) { + visited.add(node.getId()); + + for (String neighbourId: node.getNeighbours()) { + Node neighbour = getNode(neighbourId); + if (neighbour == null || visited.contains(neighbourId)) continue; + rec(neighbour, visited, result); + } + + result.add(node); + } + + public void addNode(Node node) { + nodes.put(node.getId(), node); + } + + public void addNodes(Node... nodes) { + for (Node node: nodes) { + addNode(node); + } + } + + public Node getNode(String id) { + return nodes.get(id); + } + + public Map getNodes() { + return nodes; + } + + public void setNodes(Map nodes) { + this.nodes = nodes; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/api_workflow/Node.java b/libs/dao/src/main/java/com/akto/dto/api_workflow/Node.java new file mode 100644 index 0000000000..ef3a0678d1 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/api_workflow/Node.java @@ -0,0 +1,54 @@ +package com.akto.dto.api_workflow; + +import com.akto.dto.testing.WorkflowNodeDetails; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class Node { + private String id; + private WorkflowNodeDetails workflowNodeDetails; + private Set neighbours = new HashSet<>(); + + public Node(String id, WorkflowNodeDetails workflowNodeDetails) { + this.id = id; + this.workflowNodeDetails = workflowNodeDetails; + } + + public void addNeighbours(String... ids) { + neighbours.addAll(Arrays.asList(ids)); + } + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public WorkflowNodeDetails getWorkflowNodeDetails() { + return workflowNodeDetails; + } + + public void setWorkflowNodeDetails(WorkflowNodeDetails workflowNodeDetails) { + this.workflowNodeDetails = workflowNodeDetails; + } + + public Set getNeighbours() { + return neighbours; + } + + public void setNeighbours(Set neighbours) { + this.neighbours = neighbours; + } + + @Override + public String toString() { + return "Node{" + + "id='" + id + '\'' + + '}'; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/auth/APIAuth.java b/libs/dao/src/main/java/com/akto/dto/auth/APIAuth.java new file mode 100644 index 0000000000..25d8a38a07 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/auth/APIAuth.java @@ -0,0 +1,27 @@ +package com.akto.dto.auth; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +@BsonDiscriminator +public abstract class APIAuth { + public enum Type { + BASIC, OAUTH2, APIKEY + } + + int id; + Type type; + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + abstract public void setType(Type type); + + public Type getType() { + return this.type; + } +} \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dto/auth/APIAuthAPIKey.java b/libs/dao/src/main/java/com/akto/dto/auth/APIAuthAPIKey.java new file mode 100644 index 0000000000..b65fc28517 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/auth/APIAuthAPIKey.java @@ -0,0 +1,56 @@ +package com.akto.dto.auth; + +import com.akto.dao.context.Context; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +@BsonDiscriminator +public class APIAuthAPIKey extends APIAuth { + + public enum Placement { + HEADER, QUERY, COOKIE + } + + String authKey; + Placement placement; + String keyName; + + public APIAuthAPIKey() {} + + public APIAuthAPIKey(String authKey, Placement placement, String keyName) { + this.authKey = authKey; + this.placement = placement; + this.keyName = keyName; + this.type = Type.APIKEY; + this.id = Context.now(); + } + + @Override + public void setType(Type type) { + this.type = Type.APIKEY; + } + + public String getAuthKey() { + return this.authKey; + } + + public void setAuthKey(String authKey) { + this.authKey = authKey; + } + + public Placement getPlacement() { + return this.placement; + } + + public void setPlacement(Placement placement) { + this.placement = placement; + } + + public String getKeyName() { + return this.keyName; + } + + public void setKeyName(String keyName) { + this.keyName = keyName; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/auth/APIAuthBasic.java b/libs/dao/src/main/java/com/akto/dto/auth/APIAuthBasic.java new file mode 100644 index 0000000000..23f49a8483 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/auth/APIAuthBasic.java @@ -0,0 +1,43 @@ +package com.akto.dto.auth; + +import com.akto.dao.context.Context; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +@BsonDiscriminator +public class APIAuthBasic extends APIAuth { + String username; + String password; + + public APIAuthBasic() { + } + + public APIAuthBasic(String username, String password) { + this.username = username; + this.password = password; + this.type = Type.BASIC; + this.id = Context.now(); + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public void setType(Type type) { + this.type = Type.BASIC; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/auth/APIAuthOAuth.java b/libs/dao/src/main/java/com/akto/dto/auth/APIAuthOAuth.java new file mode 100644 index 0000000000..49571e4818 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/auth/APIAuthOAuth.java @@ -0,0 +1,33 @@ +package com.akto.dto.auth; + +import com.akto.dao.context.Context; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +@BsonDiscriminator +public class APIAuthOAuth extends APIAuth { + + String oauthKey; + + public APIAuthOAuth() {} + + public APIAuthOAuth(String oauthKey) { + this.oauthKey = oauthKey; + this.type = Type.OAUTH2; + this.id = Context.now(); + } + + public String getOauthKey() { + return this.oauthKey; + } + + public void setOauthKey(String oauthKey) { + this.oauthKey = oauthKey; + } + + @Override + public void setType(Type type) { + this.type = Type.OAUTH2; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/data_types/BelongsToPredicate.java b/libs/dao/src/main/java/com/akto/dto/data_types/BelongsToPredicate.java new file mode 100644 index 0000000000..02e5228cc9 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/data_types/BelongsToPredicate.java @@ -0,0 +1,34 @@ +package com.akto.dto.data_types; + +import com.akto.dto.ApiInfo; + +import java.util.Set; + +public class BelongsToPredicate extends Predicate{ + + private Set value; + + public BelongsToPredicate() { + super(Type.BELONGS_TO); + } + public BelongsToPredicate(Set value) { + super(Type.BELONGS_TO); + this.value = value; + } + + @Override + public boolean validate(Object value) { + if (this.value != null) { + return this.value.contains(value); + } + return false; + } + + public Set getValue() { + return value; + } + + public void setValue(Set value) { + this.value = value; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/data_types/Conditions.java b/libs/dao/src/main/java/com/akto/dto/data_types/Conditions.java new file mode 100644 index 0000000000..b8026f0cea --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/data_types/Conditions.java @@ -0,0 +1,64 @@ +package com.akto.dto.data_types; + +import java.util.List; + + +public class Conditions { + List predicates; + Operator operator; + + public Conditions() { + } + + public Conditions(List predicates, Operator operator) { + this.predicates = predicates; + this.operator = operator; + } + + public boolean validate(Object value) { + if (predicates == null || predicates.size() == 0) return false; + boolean result = predicates.get(0).validate(value); + for (int i = 1 ; i < this.predicates.size(); i++) { + Predicate predicate = predicates.get(i); + switch (operator) { + case AND: + result = result && predicate.validate(value); + break; + case OR: + result = result || predicate.validate(value); + break; + } + } + + return result; + } + + public enum Operator { + AND, OR + } + + public List getPredicates() { + return predicates; + } + + public void setPredicates(List predicates) { + this.predicates = predicates; + } + + public Operator getOperator() { + return operator; + } + + public void setOperator(Operator operator) { + this.operator = operator; + } + + + @Override + public String toString() { + return "{" + + " predicates='" + getPredicates() + "'" + + ", operator='" + getOperator() + "'" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/data_types/ContainsPredicate.java b/libs/dao/src/main/java/com/akto/dto/data_types/ContainsPredicate.java new file mode 100644 index 0000000000..d009756f5b --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/data_types/ContainsPredicate.java @@ -0,0 +1,37 @@ +package com.akto.dto.data_types; + +import com.akto.dto.ApiInfo; + +public class ContainsPredicate extends Predicate{ + + private String value; + + + public ContainsPredicate() { + super(Type.CONTAINS); + } + + public ContainsPredicate(String value) { + super(Type.CONTAINS); + this.value = value; + } + + @Override + public boolean validate(Object value) { + if (value instanceof ApiInfo.ApiInfoKey) { + ApiInfo.ApiInfoKey infoKey = (ApiInfo.ApiInfoKey) value; + return infoKey.getUrl() != null && infoKey.getUrl().contains(this.value); + } else if (value instanceof String) { + return ((String) value).contains(this.value); + } + return false; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/data_types/EndsWithPredicate.java b/libs/dao/src/main/java/com/akto/dto/data_types/EndsWithPredicate.java new file mode 100644 index 0000000000..4765ffe7a2 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/data_types/EndsWithPredicate.java @@ -0,0 +1,30 @@ +package com.akto.dto.data_types; + +public class EndsWithPredicate extends Predicate{ + String value; + + public EndsWithPredicate() { + super(Type.ENDS_WITH); + } + + public EndsWithPredicate(String value) { + super(Type.ENDS_WITH); + this.value = value; + } + + @Override + public boolean validate(Object obj) { + if (!(obj instanceof String)) return false; + + String str = obj.toString(); + return str.endsWith(this.value); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/data_types/EqualsToPredicate.java b/libs/dao/src/main/java/com/akto/dto/data_types/EqualsToPredicate.java new file mode 100644 index 0000000000..1e08dcd09b --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/data_types/EqualsToPredicate.java @@ -0,0 +1,32 @@ +package com.akto.dto.data_types; + +import java.util.regex.Pattern; + +public class EqualsToPredicate extends Predicate{ + private String value; + + public EqualsToPredicate() { + super(Type.EQUALS_TO); + } + + public EqualsToPredicate(String value) { + super(Type.EQUALS_TO); + this.value = value; + } + + @Override + public boolean validate(Object obj) { + if (!(obj instanceof String)) return false; + String str = obj.toString(); + str = str.trim(); + return str.equals(this.value); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/data_types/IsNumberPredicate.java b/libs/dao/src/main/java/com/akto/dto/data_types/IsNumberPredicate.java new file mode 100644 index 0000000000..affce68de3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/data_types/IsNumberPredicate.java @@ -0,0 +1,13 @@ +package com.akto.dto.data_types; + +public class IsNumberPredicate extends Predicate{ + + public IsNumberPredicate() { + super(Type.IS_NUMBER); + } + + @Override + public boolean validate(Object obj) { + return (obj instanceof Integer) || (obj instanceof Double); + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/data_types/NotBelongsToPredicate.java b/libs/dao/src/main/java/com/akto/dto/data_types/NotBelongsToPredicate.java new file mode 100644 index 0000000000..2426f60161 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/data_types/NotBelongsToPredicate.java @@ -0,0 +1,35 @@ +package com.akto.dto.data_types; + +import com.akto.dto.ApiInfo; + +import java.util.Set; + +public class NotBelongsToPredicate extends Predicate{ + + private Set value; + + public NotBelongsToPredicate(Set value) { + super(Type.NOT_BELONGS_TO); + this.value = value; + } + public NotBelongsToPredicate() { + super(Type.NOT_BELONGS_TO); + this.value = null; + } + + @Override + public boolean validate(Object value) { + if (this.value != null) {//Replacing instance for generic type + return !this.value.contains(value); + } + return true; + } + + public Set getValue() { + return value; + } + + public void setValue(Set value) { + this.value = value; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/data_types/Predicate.java b/libs/dao/src/main/java/com/akto/dto/data_types/Predicate.java new file mode 100644 index 0000000000..5b328f091e --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/data_types/Predicate.java @@ -0,0 +1,107 @@ +package com.akto.dto.data_types; + +import com.akto.dto.ApiInfo; +import com.akto.dto.type.URLMethods; +import com.mongodb.BasicDBObject; + +import java.util.*; + +public abstract class Predicate { + private Type type; + public static final String VALUE = "value"; + public static final String TYPE = "type"; + + public enum Type { + REGEX, STARTS_WITH, ENDS_WITH, IS_NUMBER, EQUALS_TO, CONTAINS, BELONGS_TO, NOT_BELONGS_TO + } + + public Predicate(Type type) { + this.type = type; + } + + public abstract boolean validate(Object value); + + public Type getType() { + return type; + } + + public static Predicate generatePredicate(Predicate.Type type, Map valueMap) { + if (valueMap == null || type == null) return null; + Predicate predicate; + String value; + switch (type) { + case REGEX: + Object regex = valueMap.get(VALUE); + if (!(regex instanceof String)) return null; + value = regex.toString(); + if (value.length() == 0) return null; + if (value.startsWith("/")) { + value = value.substring(1, value.lastIndexOf("/")); + } + predicate = new RegexPredicate(value); + return predicate; + case STARTS_WITH: + Object prefix = valueMap.get(VALUE); + if (!(prefix instanceof String)) return null; + value = prefix.toString(); + if (value.length() == 0) return null; + predicate = new StartsWithPredicate(value); + return predicate; + case ENDS_WITH: + Object suffix = valueMap.get(VALUE); + if (!(suffix instanceof String)) return null; + value = suffix.toString(); + if (value.length() == 0) return null; + predicate = new EndsWithPredicate(value); + return predicate; + case EQUALS_TO: + Object v = valueMap.get(VALUE); + if (!(v instanceof String)) return null; + value = v.toString(); + if (value.length() == 0) return null; + predicate = new EqualsToPredicate(value); + return predicate; + case IS_NUMBER: + return new IsNumberPredicate(); + case CONTAINS: + Object contain = valueMap.get(VALUE); + if (! (contain instanceof String)) return null; + return new ContainsPredicate((String) contain); + case BELONGS_TO: + Object belongsTo = valueMap.get(VALUE); + Set set = createApiInfoSetFromMap(belongsTo); + if (set == null) return null; + return new BelongsToPredicate(set); + case NOT_BELONGS_TO: + Object notBelongTo = valueMap.get(VALUE); + Set setNotBelong = createApiInfoSetFromMap(notBelongTo); + return new NotBelongsToPredicate(setNotBelong); + + default: + return null; + } + } + private static Set createApiInfoSetFromMap(Object value) { + if (!(value instanceof List)) { + return null; + } + HashSet set = new HashSet<>(); + List listOfValues = (List) value; + for (Object obj : listOfValues) { + if (obj instanceof HashMap) { + HashMap map = (HashMap) obj; + BasicDBObject item = new BasicDBObject(); + item.putAll(map); + ApiInfo.ApiInfoKey infoKey = new ApiInfo.ApiInfoKey( + item.getInt(ApiInfo.ApiInfoKey.API_COLLECTION_ID), + item.getString(ApiInfo.ApiInfoKey.URL), + URLMethods.Method.fromString(item.getString(ApiInfo.ApiInfoKey.METHOD))); + set.add(infoKey); + } + } + return set; + } + public void setType(Type type) { + this.type = type; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/data_types/RegexPredicate.java b/libs/dao/src/main/java/com/akto/dto/data_types/RegexPredicate.java new file mode 100644 index 0000000000..da3bd27ead --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/data_types/RegexPredicate.java @@ -0,0 +1,40 @@ +package com.akto.dto.data_types; + +import java.util.regex.Pattern; + +public class RegexPredicate extends Predicate{ + private String value; + + public RegexPredicate() { + super(Type.REGEX); + } + + public RegexPredicate(String value) { + super(Type.REGEX); + this.value = value; + } + + @Override + public boolean validate(Object obj) { + if (!(obj instanceof String)) return false; + String str = obj.toString(); + Pattern pattern = Pattern.compile(this.value); + return pattern.matcher(str).matches(); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "{" + + " value='" + getValue() + "'" + + "}"; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/data_types/StartsWithPredicate.java b/libs/dao/src/main/java/com/akto/dto/data_types/StartsWithPredicate.java new file mode 100644 index 0000000000..2ac0bb01c7 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/data_types/StartsWithPredicate.java @@ -0,0 +1,30 @@ +package com.akto.dto.data_types; + +public class StartsWithPredicate extends Predicate{ + String value; + + public StartsWithPredicate() { + super(Type.STARTS_WITH); + } + + public StartsWithPredicate(String value) { + super(Type.STARTS_WITH); + this.value = value; + } + + @Override + public boolean validate(Object obj) { + if (!(obj instanceof String)) return false; + + String str = obj.toString(); + return str.startsWith(this.value); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/notifications/CustomWebhook.java b/libs/dao/src/main/java/com/akto/dto/notifications/CustomWebhook.java new file mode 100644 index 0000000000..97d18aa441 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/notifications/CustomWebhook.java @@ -0,0 +1,154 @@ +package com.akto.dto.notifications; + +import org.bson.codecs.pojo.annotations.BsonId; + +import com.akto.dto.type.URLMethods.Method; + +public class CustomWebhook { + + @BsonId + int id; + + String webhookName; + String url; + String headerString; + String queryParams; + String body; + Method method; + int frequencyInSeconds; + String userEmail; + int createTime; + int lastUpdateTime; + int lastSentTimestamp; + public enum ActiveStatus{ + ACTIVE,INACTIVE; + } + ActiveStatus activeStatus; + + public CustomWebhook(){ + + } + + public CustomWebhook(int id, String webhookName, String url, String headerString, String queryParams, String body, + Method method, int frequencyInSeconds, String userEmail, int createTime, int lastUpdateTime, + int lastSentTimestamp, ActiveStatus activeStatus) { + this.id = id; + this.webhookName = webhookName; + this.url = url; + this.headerString = headerString; + this.queryParams = queryParams; + this.body = body; + this.method = method; + this.frequencyInSeconds = frequencyInSeconds; + this.userEmail = userEmail; + this.createTime = createTime; + this.lastUpdateTime = lastUpdateTime; + this.lastSentTimestamp = lastSentTimestamp; + this.activeStatus = activeStatus; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getWebhookName() { + return webhookName; + } + + public void setWebhookName(String webhookName) { + this.webhookName = webhookName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getHeaderString() { + return headerString; + } + + public void setHeaderString(String headerString) { + this.headerString = headerString; + } + + public String getQueryParams() { + return queryParams; + } + + public void setQueryParams(String queryParams) { + this.queryParams = queryParams; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public Method getMethod() { + return method; + } + + public void setMethod(Method method) { + this.method = method; + } + + public int getFrequencyInSeconds() { + return frequencyInSeconds; + } + + public void setFrequencyInSeconds(int frequencyInSeconds) { + this.frequencyInSeconds = frequencyInSeconds; + } + + public String getUserEmail() { + return userEmail; + } + + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + + public int getCreateTime() { + return createTime; + } + + public void setCreateTime(int createTime) { + this.createTime = createTime; + } + + public int getLastUpdateTime() { + return lastUpdateTime; + } + + public void setLastUpdateTime(int lastUpdateTime) { + this.lastUpdateTime = lastUpdateTime; + } + + public int getLastSentTimestamp() { + return lastSentTimestamp; + } + + public void setLastSentTimestamp(int lastSentTimestamp) { + this.lastSentTimestamp = lastSentTimestamp; + } + + public ActiveStatus getActiveStatus() { + return activeStatus; + } + + public void setActiveStatus(ActiveStatus activeStatus) { + this.activeStatus = activeStatus; + } + +} \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dto/notifications/CustomWebhookResult.java b/libs/dao/src/main/java/com/akto/dto/notifications/CustomWebhookResult.java new file mode 100644 index 0000000000..426ef3524c --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/notifications/CustomWebhookResult.java @@ -0,0 +1,65 @@ +package com.akto.dto.notifications; + +import java.util.List; + +import org.bson.types.ObjectId; + +public class CustomWebhookResult { + + ObjectId id; + + int webhookId; + String userEmail; + int timestamp; + String message; + List errors; + + public CustomWebhookResult() { + } + + public CustomWebhookResult(int webhookId, String userEmail, int timestamp, String message, + List errors) { + this.webhookId = webhookId; + this.userEmail = userEmail; + this.timestamp = timestamp; + this.message = message; + this.errors = errors; + } + + public ObjectId getId() { + return id; + } + public void setId(ObjectId id) { + this.id = id; + } + public int getWebhookId() { + return webhookId; + } + public void setWebhookId(int webhookId) { + this.webhookId = webhookId; + } + public String getUserEmail() { + return userEmail; + } + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + public int getTimestamp() { + return timestamp; + } + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + public String getMessage() { + return message; + } + public void setMessage(String message) { + this.message = message; + } + public List getErrors() { + return errors; + } + public void setErrors(List errors) { + this.errors = errors; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/notifications/SlackWebhook.java b/libs/dao/src/main/java/com/akto/dto/notifications/SlackWebhook.java new file mode 100644 index 0000000000..c1cb1be9fa --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/notifications/SlackWebhook.java @@ -0,0 +1,120 @@ +package com.akto.dto.notifications; + +import org.bson.codecs.pojo.annotations.BsonId; + +public class SlackWebhook { + + @BsonId + int id; + + String webhook; + int smallerDuration; + int largerDuration; + int timestamp; + String userEmail; + String dashboardUrl; + int lastSentTimestamp; + int frequencyInSeconds; + + public SlackWebhook() { + } + + public SlackWebhook(int id, String webhook, int smallerDuration, int largerDuration, int timestamp, String userEmail, String dashboardUrl, int lastSentTimestamp, int frequencyInSeconds) { + this.id = id; + this.webhook = webhook; + this.smallerDuration = smallerDuration; + this.largerDuration = largerDuration; + this.timestamp = timestamp; + this.userEmail = userEmail; + this.dashboardUrl = dashboardUrl; + this.lastSentTimestamp = lastSentTimestamp; + this.frequencyInSeconds = frequencyInSeconds; + } + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + public String getWebhook() { + return this.webhook; + } + + public void setWebhook(String webhook) { + this.webhook = webhook; + } + + public int getSmallerDuration() { + return this.smallerDuration; + } + + public void setSmallerDuration(int smallerDuration) { + this.smallerDuration = smallerDuration; + } + + public int getLargerDuration() { + return this.largerDuration; + } + + public void setLargerDuration(int largerDuration) { + this.largerDuration = largerDuration; + } + + public int getTimestamp() { + return this.timestamp; + } + + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + + public String getUserEmail() { + return this.userEmail; + } + + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + + public String getDashboardUrl() { + return this.dashboardUrl; + } + + public void setDashboardUrl(String dashboardUrl) { + this.dashboardUrl = dashboardUrl; + } + + public int getLastSentTimestamp() { + return this.lastSentTimestamp; + } + + public void setLastSentTimestamp(int lastSentTimestamp) { + this.lastSentTimestamp = lastSentTimestamp; + } + + public int getFrequencyInSeconds() { + return this.frequencyInSeconds; + } + + public void setFrequencyInSeconds(int frequencyInSeconds) { + this.frequencyInSeconds = frequencyInSeconds; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", webhook='" + getWebhook() + "'" + + ", smallerDuration='" + getSmallerDuration() + "'" + + ", largerDuration='" + getLargerDuration() + "'" + + ", timestamp='" + getTimestamp() + "'" + + ", userEmail='" + getUserEmail() + "'" + + ", dashboardUrl='" + getDashboardUrl() + "'" + + ", LastSentTimestamp='" + getLastSentTimestamp() + "'" + + ", FrequencyInSeconds='" + getFrequencyInSeconds() + "'" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/pii/PIISource.java b/libs/dao/src/main/java/com/akto/dto/pii/PIISource.java new file mode 100644 index 0000000000..eea3dbc8cf --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/pii/PIISource.java @@ -0,0 +1,96 @@ +package com.akto.dto.pii; + +import java.util.Map; + +import org.bson.codecs.pojo.annotations.BsonId; + +public class PIISource { + @BsonId + private String id; + + private String fileUrl; + private int lastSynced; + private int addedByUser; + private int startTs; + private boolean active; + private Map mapNameToPIIType; + + public PIISource() { + } + + public PIISource(String fileUrl, int lastSynced, int addedByUser, int startTs, Map mapNameToPIIType, boolean active) { + this.fileUrl = fileUrl; + this.lastSynced = lastSynced; + this.addedByUser = addedByUser; + this.startTs = startTs; + this.mapNameToPIIType = mapNameToPIIType; + this.active = active; + } + + public String getFileUrl() { + return this.fileUrl; + } + + public void setFileUrl(String fileUrl) { + this.fileUrl = fileUrl; + } + + public int getLastSynced() { + return this.lastSynced; + } + + public void setLastSynced(int lastSynced) { + this.lastSynced = lastSynced; + } + + public int getAddedByUser() { + return this.addedByUser; + } + + public void setAddedByUser(int addedByUser) { + this.addedByUser = addedByUser; + } + + public int getStartTs() { + return this.startTs; + } + + public void setStartTs(int startTs) { + this.startTs = startTs; + } + + public Map getMapNameToPIIType() { + return this.mapNameToPIIType; + } + + public void setMapNameToPIIType(Map mapNameToPIIType) { + this.mapNameToPIIType = mapNameToPIIType; + } + + public boolean getActive() { + return this.active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String toString() { + return "{" + + " fileUrl='" + getFileUrl() + "'" + + ", lastSynced='" + getLastSynced() + "'" + + ", addedByUser='" + getAddedByUser() + "'" + + ", startTs='" + getStartTs() + "'" + + ", mapNameToPIIType='" + getMapNameToPIIType() + "'" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/pii/PIIType.java b/libs/dao/src/main/java/com/akto/dto/pii/PIIType.java new file mode 100644 index 0000000000..8a004cfc88 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/pii/PIIType.java @@ -0,0 +1,85 @@ +package com.akto.dto.pii; + +import java.util.Objects; + +import com.akto.dto.type.SingleTypeInfo.SubType; + +public class PIIType extends SubType { + private String name; + private boolean isSensitive; + private String regexPattern; + private boolean onKey; + + public PIIType() { + } + + public PIIType(String name, boolean isSensitive, String regexPattern, boolean onKey) { + this.name = name; + this.isSensitive = isSensitive; + this.regexPattern = regexPattern; + this.onKey = onKey; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean getIsSensitive() { + return this.isSensitive; + } + + public void setIsSensitive(boolean isSensitive) { + this.isSensitive = isSensitive; + } + + public String getRegexPattern() { + return this.regexPattern; + } + + public void setRegexPattern(String regexPattern) { + this.regexPattern = regexPattern; + } + + public boolean isOnKey() { + return this.onKey; + } + + public boolean getOnKey() { + return this.onKey; + } + + public void setOnKey(boolean onKey) { + this.onKey = onKey; + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof PIIType)) { + return false; + } + PIIType pIIType = (PIIType) o; + return Objects.equals(name, pIIType.name) && isSensitive == pIIType.isSensitive && Objects.equals(regexPattern, pIIType.regexPattern) && onKey == pIIType.onKey; + } + + @Override + public int hashCode() { + return Objects.hash(name, isSensitive, regexPattern, onKey); + } + + @Override + public String toString() { + return "{" + + " name='" + getName() + "'" + + ", isSensitive='" + getIsSensitive() + "'" + + ", regexPattern='" + getRegexPattern() + "'" + + ", onKey='" + isOnKey() + "'" + + "}"; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/runtime_filters/FieldExistsFilter.java b/libs/dao/src/main/java/com/akto/dto/runtime_filters/FieldExistsFilter.java new file mode 100644 index 0000000000..c02c0bed18 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/runtime_filters/FieldExistsFilter.java @@ -0,0 +1,77 @@ +package com.akto.dto.runtime_filters; + +import com.akto.dto.CustomFilter; +import com.akto.dto.HttpResponseParams; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; + +import java.io.IOException; +import java.util.*; + +public class FieldExistsFilter extends CustomFilter { + private String fieldName; + + private static final ObjectMapper mapper = new ObjectMapper(); + private static final JsonFactory factory = mapper.getFactory(); + + public FieldExistsFilter() { + super(); + } + + public FieldExistsFilter(String fieldName) { + super(); + this.fieldName = fieldName; + } + + + @Override + public boolean process(HttpResponseParams httpResponseParams) { + JsonParser jp = null; + JsonNode node; + try { + jp = factory.createParser(httpResponseParams.getPayload()); + node = mapper.readTree(jp); + } catch (IOException e) { + ; + return false; + } + + return findField(node, this.fieldName); + } + + public static boolean findField(JsonNode node, String matchFieldName) { + if (node == null) return false; + + if (node.isArray()) { + ArrayNode arrayNode = (ArrayNode) node; + for (int i = 0; i < arrayNode.size(); i++) { + JsonNode arrayElement = arrayNode.get(i); + boolean result = findField(arrayElement, matchFieldName); + if (result) return true; + } + } else { + Iterator fieldNames = node.fieldNames(); + while(fieldNames.hasNext()) { + String fieldName = fieldNames.next(); + if (Objects.equals(fieldName, matchFieldName)) return true; + JsonNode fieldValue = node.get(fieldName); + boolean result= findField(fieldValue, matchFieldName); + if (result) return true; + } + } + + + return false; + } + + public String getFieldName() { + return fieldName; + } + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/runtime_filters/ResponseCodeRuntimeFilter.java b/libs/dao/src/main/java/com/akto/dto/runtime_filters/ResponseCodeRuntimeFilter.java new file mode 100644 index 0000000000..a7f0e836f3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/runtime_filters/ResponseCodeRuntimeFilter.java @@ -0,0 +1,42 @@ +package com.akto.dto.runtime_filters; + +import com.akto.dto.CustomFilter; +import com.akto.dto.HttpResponseParams; + +public class ResponseCodeRuntimeFilter extends CustomFilter { + + private int startValue; + private int endValue; + + public ResponseCodeRuntimeFilter() { + super(); + } + + public ResponseCodeRuntimeFilter( int startValue, int endValue) { + super(); + this.startValue = startValue; + this.endValue = endValue; + } + + @Override + public boolean process(HttpResponseParams httpResponseParams) { + int responseCode = httpResponseParams.getStatusCode(); + return responseCode >= startValue && responseCode <= endValue; + } + + public int getStartValue() { + return startValue; + } + + public void setStartValue(int startValue) { + this.startValue = startValue; + } + + public int getEndValue() { + return endValue; + } + + public void setEndValue(int endValue) { + this.endValue = endValue; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/runtime_filters/RuntimeFilter.java b/libs/dao/src/main/java/com/akto/dto/runtime_filters/RuntimeFilter.java new file mode 100644 index 0000000000..8060d6e48d --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/runtime_filters/RuntimeFilter.java @@ -0,0 +1,136 @@ +package com.akto.dto.runtime_filters; + +import com.akto.dto.CustomFilter; +import com.akto.dto.HttpResponseParams; + +import java.util.Collections; +import java.util.List; + +public class RuntimeFilter { + private int id; + public static final String NAME = "name"; + private String name; + + private List customFilterList; + private Operator customFiltersOperator; + + private UseCase useCase; + + private String customFieldName; + + public enum UseCase { + AUTH_TYPE, SET_CUSTOM_FIELD, DETERMINE_API_ACCESS_TYPE + } + + public enum Operator { + AND, OR + } + + public RuntimeFilter(int id,UseCase useCase, String name, List customFilterList, + Operator customFiltersOperator, String customFieldName) { + this.id = id; + this.name = name; + this.useCase = useCase; + this.customFilterList = customFilterList; + this.customFiltersOperator = customFiltersOperator; + this.customFieldName = customFieldName; + } + + public RuntimeFilter() { + } + + public boolean process(HttpResponseParams httpResponseParams) { + if (this.customFilterList == null || this.customFilterList.size() == 0) return false; + + boolean result = this.customFilterList.get(0).process(httpResponseParams); + for (int i = 1 ; i < this.customFilterList.size(); i++) { + CustomFilter customFilter = this.customFilterList.get(i); + switch (customFiltersOperator) { + case AND: + result = result && customFilter.process(httpResponseParams); + break; + case OR: + result = result || customFilter.process(httpResponseParams); + break; + } + } + return result; + } + + + + public static final String OPEN_ENDPOINTS_FILTER = "Open Endpoints"; + public static RuntimeFilter generateOpenEndpointsFilter(int id) { + List customFilterList = Collections.singletonList(new ResponseCodeRuntimeFilter(200, 299)); + return new RuntimeFilter( + id, + RuntimeFilter.UseCase.AUTH_TYPE, + OPEN_ENDPOINTS_FILTER, + customFilterList, + RuntimeFilter.Operator.AND, + "unauthenticated" + ); + } + + public static final String API_ACCESS_TYPE_FILTER = "API access Type"; + public static RuntimeFilter generateApiAccessTypeFilter(int id) { + List customFilterList = Collections.singletonList(new ResponseCodeRuntimeFilter(200, 299)); + return new RuntimeFilter( + id, + UseCase.DETERMINE_API_ACCESS_TYPE, + API_ACCESS_TYPE_FILTER, + customFilterList, + RuntimeFilter.Operator.AND, + "access_type" + ); + } + + + public void setCustomFilterList(List customFilterList) { + this.customFilterList = customFilterList; + } + + public List getCustomFilterList() { + return customFilterList; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public UseCase getUseCase() { + return useCase; + } + + public void setUseCase(UseCase useCase) { + this.useCase = useCase; + } + + public Operator getCustomFiltersOperator() { + return customFiltersOperator; + } + + public void setCustomFiltersOperator(Operator customFiltersOperator) { + this.customFiltersOperator = customFiltersOperator; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCustomFieldName() { + return customFieldName; + } + + public void setCustomFieldName(String customFieldName) { + this.customFieldName = customFieldName; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/test_run_findings/TestingIssuesId.java b/libs/dao/src/main/java/com/akto/dto/test_run_findings/TestingIssuesId.java new file mode 100644 index 0000000000..8655d25bb1 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/test_run_findings/TestingIssuesId.java @@ -0,0 +1,104 @@ +package com.akto.dto.test_run_findings; + +import com.akto.dto.ApiInfo.ApiInfoKey; +import com.akto.dto.testing.sources.TestSourceConfig; +import com.akto.util.enums.GlobalEnums.TestSubCategory; +import org.bson.codecs.pojo.annotations.BsonIgnore; + +import java.util.Objects; + +import static com.akto.util.enums.GlobalEnums.TestErrorSource; + +public class TestingIssuesId { + public static final String API_KEY_INFO = "apiInfoKey"; + private ApiInfoKey apiInfoKey; + private TestErrorSource testErrorSource; + public static final String TEST_SUB_CATEGORY = "testSubCategory"; + private TestSubCategory testSubCategory; + public static final String TEST_CATEGORY_FROM_SOURCE_CONFIG = "testCategoryFromSourceConfig"; + private String testCategoryFromSourceConfig; + @BsonIgnore + private TestSourceConfig testSourceConfig; + + public TestingIssuesId(ApiInfoKey apiInfoKey, TestErrorSource source, TestSubCategory category) { + this.apiInfoKey = apiInfoKey; + this.testErrorSource = source; + this.testSubCategory = category; + } + + public TestingIssuesId(ApiInfoKey apiInfoKey, TestErrorSource source, TestSubCategory category, String testCategoryFromSourceConfig) { + this.apiInfoKey = apiInfoKey; + this.testErrorSource = source; + this.testSubCategory = category; + this.testCategoryFromSourceConfig = testCategoryFromSourceConfig; + } + + public TestingIssuesId() { + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (o instanceof TestingIssuesId) { + TestingIssuesId id = (TestingIssuesId) o; + return id.apiInfoKey.equals(this.apiInfoKey) + && id.testSubCategory == this.testSubCategory + && id.testErrorSource == this.testErrorSource + && Objects.equals(id.testCategoryFromSourceConfig, this.testCategoryFromSourceConfig); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(apiInfoKey, testErrorSource, testSubCategory); + } + + @Override + public String toString() { + return "{ApiInfoKey : " + this.apiInfoKey.toString() + ", testSubCategory : " + testSubCategory.name() + + ", testErrorSource : " + testErrorSource.name(); + } + + public void setApiInfoKey(ApiInfoKey apiInfoKey) { + this.apiInfoKey = apiInfoKey; + } + + public void setTestErrorSource(TestErrorSource testErrorSource) { + this.testErrorSource = testErrorSource; + } + + public void setTestSubCategory(TestSubCategory testSubCategory) { + this.testSubCategory = testSubCategory; + } + + public ApiInfoKey getApiInfoKey() { + return apiInfoKey; + } + + public TestErrorSource getTestErrorSource() { + return testErrorSource; + } + + public TestSubCategory getTestSubCategory() { + return testSubCategory; + } + + + public String getTestCategoryFromSourceConfig() { + return testCategoryFromSourceConfig; + } + + public void setTestCategoryFromSourceConfig(String testCategoryFromSourceConfig) { + this.testCategoryFromSourceConfig = testCategoryFromSourceConfig; + } + + public TestSourceConfig getTestSourceConfig() { + return testSourceConfig; + } + + public void setTestSourceConfig(TestSourceConfig testSourceConfig) { + this.testSourceConfig = testSourceConfig; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/test_run_findings/TestingRunIssues.java b/libs/dao/src/main/java/com/akto/dto/test_run_findings/TestingRunIssues.java new file mode 100644 index 0000000000..c67da6c5b6 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/test_run_findings/TestingRunIssues.java @@ -0,0 +1,90 @@ +package com.akto.dto.test_run_findings; + +import com.akto.util.enums.GlobalEnums; +import org.bson.types.ObjectId; + +public class TestingRunIssues { + public static final String TEST_RUN_ISSUES_STATUS = "testRunIssueStatus"; + + private GlobalEnums.TestRunIssueStatus testRunIssueStatus; + private TestingIssuesId id; + public static final String LAST_SEEN = "lastSeen"; + private int lastSeen; + public static final String CREATION_TIME = "creationTime"; + private int creationTime; + public static final String KEY_SEVERITY = "severity"; + private GlobalEnums.Severity severity; + public static final String LATEST_TESTING_RUN_SUMMARY_ID = "latestTestingRunSummaryId"; + private ObjectId latestTestingRunSummaryId; + public static final String IGNORE_REASON = "ignoreReason"; + private String ignoreReason; + + public TestingRunIssues(TestingIssuesId id, GlobalEnums.Severity severity, GlobalEnums.TestRunIssueStatus status, + int creationTime, int lastSeen, ObjectId latestTestingRunSummaryId) { + this.creationTime = creationTime; + this.lastSeen = lastSeen; + this.id = id; + this.severity = severity; + this.testRunIssueStatus = status; + this.latestTestingRunSummaryId = latestTestingRunSummaryId; + } + + public TestingRunIssues() { + } + + public void setTestRunIssueStatus(GlobalEnums.TestRunIssueStatus testRunIssueStatus) { + this.testRunIssueStatus = testRunIssueStatus; + } + + public void setLastSeen(int lastSeen) { + this.lastSeen = lastSeen; + } + + public void setCreationTime(int creationTime) { + this.creationTime = creationTime; + } + + public void setSeverity(GlobalEnums.Severity severity) { + this.severity = severity; + } + + public void setId(TestingIssuesId id) { + this.id = id; + } + + public TestingIssuesId getId() { + return id; + } + + public int getCreationTime() { + return creationTime; + } + + public GlobalEnums.TestRunIssueStatus getTestRunIssueStatus() { + return testRunIssueStatus; + } + + public int getLastSeen() { + return lastSeen; + } + + public GlobalEnums.Severity getSeverity() { + return severity; + } + + public ObjectId getLatestTestingRunSummaryId() { + return latestTestingRunSummaryId; + } + + public void setLatestTestingRunSummaryId(ObjectId latestTestingRunSummaryId) { + this.latestTestingRunSummaryId = latestTestingRunSummaryId; + } + + public String getIgnoreReason() { + return ignoreReason; + } + + public void setIgnoreReason(String ignoreReason) { + this.ignoreReason = ignoreReason; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/AuthMechanism.java b/libs/dao/src/main/java/com/akto/dto/testing/AuthMechanism.java new file mode 100644 index 0000000000..2a0fbcd70d --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/AuthMechanism.java @@ -0,0 +1,101 @@ +package com.akto.dto.testing; + +import com.akto.dto.HttpRequestParams; +import com.akto.dto.OriginalHttpRequest; +import org.bson.types.ObjectId; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class AuthMechanism { + private ObjectId id; + private List authParams; + + private String type; + + private ArrayList requestData; + + private String uuid; + + public AuthMechanism() { + } + + public AuthMechanism(List authParams, ArrayList requestData, String type) { + this.authParams = authParams; + this.requestData = requestData; + this.type = type; + this.uuid = UUID.randomUUID().toString(); + } + + public boolean addAuthToRequest(OriginalHttpRequest request) { + for (AuthParam authParamPair : authParams) { + boolean result = authParamPair.addAuthTokens(request); + if (!result) return false; + } + return true; + } + + public boolean removeAuthFromRequest(OriginalHttpRequest request) { + for (AuthParam authParamPair : authParams) { + boolean result = authParamPair.removeAuthTokens(request); + if (!result) return false; + } + return true; + } + + public boolean authTokenPresent(OriginalHttpRequest request) { + boolean result = true; + for (AuthParam authParamPair : authParams) { + result = result && authParamPair.authTokenPresent(request); + } + return result; + } + + public ObjectId getId() { + return id; + } + + public String getUuid() { + return uuid; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public void setType(String type) { + this.type = type; + } + + public void setRequestData(ArrayList requestData) { + this.requestData = requestData; + } + + public List getAuthParams() { + return authParams; + } + + public AuthParam fetchFirstAuthParam() { + return authParams.get(0); + } + + //public AuthParam set + + public void setAuthParams(List authParams) { + this.authParams = authParams; + } + + public ArrayList getRequestData() { + return this.requestData; + } + + public String getType() { + return this.type; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/AuthParam.java b/libs/dao/src/main/java/com/akto/dto/testing/AuthParam.java new file mode 100644 index 0000000000..29659362a9 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/AuthParam.java @@ -0,0 +1,22 @@ +package com.akto.dto.testing; + +import com.akto.dto.OriginalHttpRequest; + +import java.util.ArrayList; + +public abstract class AuthParam { + + public abstract boolean addAuthTokens(OriginalHttpRequest request); + public abstract boolean removeAuthTokens(OriginalHttpRequest request); + + public abstract boolean authTokenPresent(OriginalHttpRequest request); + + public abstract String getValue(); + + public abstract void setValue(String value); + + public enum Location { + HEADER, + BODY + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/AuthParamData.java b/libs/dao/src/main/java/com/akto/dto/testing/AuthParamData.java new file mode 100644 index 0000000000..e4bbc5ea47 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/AuthParamData.java @@ -0,0 +1,55 @@ +package com.akto.dto.testing; + +public class AuthParamData { + + private AuthParam.Location where; + private String key; + + private String value; + private Boolean showHeader; + + public AuthParamData() { } + public AuthParamData(AuthParam.Location where, String key, String value, Boolean showHeader) { + this.where = where; + this.key = key; + this.value = value; + this.showHeader = showHeader; + } + + public AuthParam.Location getWhere() {return this.where;} + + public String getKey() { + return this.key; + } + + public String getValue() { + return this.value; + } + + public void setKey(String key) { + this.key = key; + } + + public void setValue(String value) { + this.value = value; + } + + public void setWhere(AuthParam.Location where) {this.where = where;} + + public Boolean validate() { + if (this.key == null || this.value == null || this.where == null) { + return false; + } + return true; + } + + public Boolean getShowHeader() { + return showHeader; + } + + public void setShowHeader(Boolean showHeader) { + this.showHeader = showHeader; + } +} + + diff --git a/libs/dao/src/main/java/com/akto/dto/testing/CollectionWiseTestingEndpoints.java b/libs/dao/src/main/java/com/akto/dto/testing/CollectionWiseTestingEndpoints.java new file mode 100644 index 0000000000..0d24e2e8d4 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/CollectionWiseTestingEndpoints.java @@ -0,0 +1,42 @@ +package com.akto.dto.testing; + + +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dto.ApiInfo; +import com.mongodb.client.model.Filters; +import org.bson.conversions.Bson; + +import java.util.List; + +public class CollectionWiseTestingEndpoints extends TestingEndpoints { + + private int apiCollectionId; + + public CollectionWiseTestingEndpoints() { + super(Type.COLLECTION_WISE); + } + + public CollectionWiseTestingEndpoints(int apiCollectionId) { + super(Type.COLLECTION_WISE); + this.apiCollectionId = apiCollectionId; + } + + @Override + public List returnApis() { + return SingleTypeInfoDao.instance.fetchEndpointsInCollection(apiCollectionId); + } + + @Override + public boolean containsApi(ApiInfo.ApiInfoKey key) { + return key.getApiCollectionId() == this.apiCollectionId; + } + + public int getApiCollectionId() { + return apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/CustomTestingEndpoints.java b/libs/dao/src/main/java/com/akto/dto/testing/CustomTestingEndpoints.java new file mode 100644 index 0000000000..00064c8f70 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/CustomTestingEndpoints.java @@ -0,0 +1,36 @@ +package com.akto.dto.testing; + +import com.akto.dto.ApiInfo; + +import java.util.List; + +public class CustomTestingEndpoints extends TestingEndpoints { + + private List apisList; + + public CustomTestingEndpoints() { + super(Type.CUSTOM); + } + + public CustomTestingEndpoints(List apisList) { + super(Type.CUSTOM); + this.apisList = apisList; + } + public List getApisList() { + return apisList; + } + + public void setApisList(List apisList) { + this.apisList = apisList; + } + + @Override + public List returnApis() { + return this.getApisList(); + } + + @Override + public boolean containsApi(ApiInfo.ApiInfoKey key) { + return this.getApisList().contains(key); + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/EndpointLogicalGroup.java b/libs/dao/src/main/java/com/akto/dto/testing/EndpointLogicalGroup.java new file mode 100644 index 0000000000..636ae86a41 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/EndpointLogicalGroup.java @@ -0,0 +1,72 @@ +package com.akto.dto.testing; + +import org.bson.types.ObjectId; + +public class EndpointLogicalGroup { + public static final String GROUP_NAME_SUFFIX = "_endpoint-logical-group"; + private ObjectId id; + private int createdTs; + private int updatedTs; + private String createdBy; + private String groupName; + private TestingEndpoints testingEndpoints; + + public EndpointLogicalGroup() {} + public EndpointLogicalGroup(ObjectId id, int createdTs,int updatedTs, String createdBy, String groupName, TestingEndpoints testingEndpoints) { + this.id = id; + this.createdTs = createdTs; + this.updatedTs = updatedTs; + this.createdBy = createdBy; + this.groupName = groupName; + this.testingEndpoints = testingEndpoints; + } + public int getCreatedTs() { + return createdTs; + } + + public void setCreatedTs(int createdTs) { + this.createdTs = createdTs; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public static final String GROUP_NAME = "groupName"; + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public TestingEndpoints getTestingEndpoints() { + return testingEndpoints; + } + + public void setTestingEndpoints(TestingEndpoints testingEndpoints) { + this.testingEndpoints = testingEndpoints; + } + + public int getUpdatedTs() { + return updatedTs; + } + + public void setUpdatedTs(int updatedTs) { + this.updatedTs = updatedTs; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/GenericTestResult.java b/libs/dao/src/main/java/com/akto/dto/testing/GenericTestResult.java new file mode 100644 index 0000000000..7791ef5bfa --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/GenericTestResult.java @@ -0,0 +1,46 @@ +package com.akto.dto.testing; + +import com.akto.dto.testing.TestResult.Confidence; + +public class GenericTestResult { + + private boolean vulnerable; + private Confidence confidence = Confidence.HIGH; + + public GenericTestResult() { + } + + public GenericTestResult(boolean vulnerable, Confidence confidence) { + this.vulnerable = vulnerable; + this.confidence = confidence; + } + + public boolean isVulnerable() { + return this.vulnerable; + } + + public boolean getVulnerable() { + return this.vulnerable; + } + + public void setVulnerable(boolean vulnerable) { + this.vulnerable = vulnerable; + } + + public Confidence getConfidence() { + return this.confidence; + } + + public void setConfidence(Confidence confidence) { + this.confidence = confidence; + } + + @Override + public String toString() { + return "{" + + " vulnerable='" + isVulnerable() + "'" + + ", confidence='" + getConfidence() + "'" + + "}"; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/HardcodedAuthParam.java b/libs/dao/src/main/java/com/akto/dto/testing/HardcodedAuthParam.java new file mode 100644 index 0000000000..522e1155b6 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/HardcodedAuthParam.java @@ -0,0 +1,86 @@ +package com.akto.dto.testing; + +import com.akto.dto.HttpRequestParams; +import com.akto.dto.OriginalHttpRequest; +import com.akto.util.JSONUtils; +import com.akto.util.JsonStringPayloadModifier; +import com.akto.util.TokenPayloadModifier; +import com.mongodb.BasicDBObject; + +import java.util.*; + +public class HardcodedAuthParam extends AuthParam { + private Location where; + private String key; + private String value; + private Boolean showHeader; + + + public HardcodedAuthParam() { } + + public HardcodedAuthParam(Location where, String key, String value, Boolean showHeader) { + this.where = where; + this.key = key; + this.value = value; + this.showHeader = showHeader; + } + + @Override + public boolean addAuthTokens(OriginalHttpRequest request) { + if (this.key == null) return false; + return TokenPayloadModifier.tokenPayloadModifier(request, this.key, this.value, this.where); + } + + @Override + public boolean removeAuthTokens(OriginalHttpRequest request) { + if (this.key == null) return false; + return TokenPayloadModifier.tokenPayloadModifier(request, this.key, null, this.where); + } + + @Override + public boolean authTokenPresent(OriginalHttpRequest request) { + if (this.key == null) return false; + String k = this.key.toLowerCase().trim(); + + if (where.toString().equals(AuthParam.Location.BODY.toString())) { + BasicDBObject basicDBObject = BasicDBObject.parse(request.getBody()); + BasicDBObject data = JSONUtils.flattenWithDots(basicDBObject); + return data.keySet().contains(this.key); + } else { + Map> headers = request.getHeaders(); + return headers.containsKey(k); + } + } + + public Location getWhere() { + return where; + } + + public void setWhere(Location where) { + this.where = where; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Boolean getShowHeader() { + return showHeader; + } + + public void setShowHeader(Boolean showHeader) { + this.showHeader = showHeader; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/LogicalGroupTestingEndpoint.java b/libs/dao/src/main/java/com/akto/dto/testing/LogicalGroupTestingEndpoint.java new file mode 100644 index 0000000000..07fccbc543 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/LogicalGroupTestingEndpoint.java @@ -0,0 +1,63 @@ +package com.akto.dto.testing; + +import com.akto.dto.ApiInfo; +import com.akto.dto.data_types.Conditions; + +import java.util.ArrayList; +import java.util.List; + +public class LogicalGroupTestingEndpoint extends TestingEndpoints { + private Conditions andConditions; + private Conditions orConditions; + + public LogicalGroupTestingEndpoint() { + super(Type.LOGICAL_GROUP); + } + + public LogicalGroupTestingEndpoint(Conditions andConditions, Conditions orConditions) { + super(Type.LOGICAL_GROUP); + this.andConditions = andConditions; + this.orConditions = orConditions; + } + + @Override + public boolean containsApi(ApiInfo.ApiInfoKey key) { + if (key == null) { + return false; + } + if (this.andConditions == null && this.orConditions == null) { + return false; + } + + boolean contains = true; + if (this.andConditions != null) { + contains = this.andConditions.validate(key); + } + if (this.orConditions != null) { + contains = contains && this.orConditions.validate(key); + } + return contains; + } + + @Override + public List returnApis() { + + return new ArrayList<>(); + } + + public Conditions getAndConditions() { + return andConditions; + } + + public void setAndConditions(Conditions andConditions) { + this.andConditions = andConditions; + } + + public Conditions getOrConditions() { + return orConditions; + } + + public void setOrConditions(Conditions orConditions) { + this.orConditions = orConditions; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/LoginFlowParams.java b/libs/dao/src/main/java/com/akto/dto/testing/LoginFlowParams.java new file mode 100644 index 0000000000..0ad88b32e9 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/LoginFlowParams.java @@ -0,0 +1,40 @@ +package com.akto.dto.testing; + +public class LoginFlowParams { + + private int userId; + private Boolean fetchValueMap; + private String nodeId; + + public LoginFlowParams() { } + + public LoginFlowParams(int userId, Boolean fetchValueMap, String nodeId) { + this.userId = userId; + this.fetchValueMap = fetchValueMap; + this.nodeId = nodeId; + } + + public int getUserId() { + return this.userId; + } + + public Boolean getFetchValueMap() { + return this.fetchValueMap; + } + + public String getNodeId() { + return this.nodeId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public void setFetchValueMap(Boolean fetchValueMap) { + this.fetchValueMap = fetchValueMap; + } + + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/LoginFlowResponse.java b/libs/dao/src/main/java/com/akto/dto/testing/LoginFlowResponse.java new file mode 100644 index 0000000000..9e39fdc1f5 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/LoginFlowResponse.java @@ -0,0 +1,45 @@ +package com.akto.dto.testing; + +import java.util.ArrayList; + +public class LoginFlowResponse { + + private ArrayList responses; + + private String error; + + private Boolean success; + + + public LoginFlowResponse() { } + public LoginFlowResponse(ArrayList responses, String error, Boolean success) { + this.responses = responses; + this.error = error; + this.success = success; + } + + public ArrayList getResponses() { + return this.responses; + } + + public Boolean getSuccess() { + return this.success; + } + + public String getError() { + return this.error; + } + + public void setResponses(ArrayList responses) { + this.responses = responses; + } + + public void setSuccess(Boolean success) { + this.success = success; + } + + public void setError(String error) { + this.error = error; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/LoginFlowStepsData.java b/libs/dao/src/main/java/com/akto/dto/testing/LoginFlowStepsData.java new file mode 100644 index 0000000000..5557da7dce --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/LoginFlowStepsData.java @@ -0,0 +1,34 @@ +package com.akto.dto.testing; + +import java.util.ArrayList; +import java.util.Map; + +public class LoginFlowStepsData { + + private Integer userID; + + private Map valuesMap; + + public LoginFlowStepsData() { } + public LoginFlowStepsData(Integer userID, Map valuesMap) { + this.userID = userID; + this.valuesMap = valuesMap; + } + + public Integer getUserID() { + return this.userID; + } + + public Map getValuesMap() { + return this.valuesMap; + } + + public void setUserID(Integer userID) { + this.userID = userID; + } + + public void setValuesMap(Map valuesMap) { + this.valuesMap = valuesMap; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/LoginRequestAuthParam.java b/libs/dao/src/main/java/com/akto/dto/testing/LoginRequestAuthParam.java new file mode 100644 index 0000000000..8bce0b19b2 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/LoginRequestAuthParam.java @@ -0,0 +1,89 @@ +package com.akto.dto.testing; + +import com.akto.dto.OriginalHttpRequest; +import com.akto.util.JSONUtils; +import com.akto.util.JsonStringPayloadModifier; +import com.akto.util.TokenPayloadModifier; +import com.mongodb.BasicDBObject; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class LoginRequestAuthParam extends AuthParam { + + private Location where; + + private String key; + private String value; + private Boolean showHeader; + + public LoginRequestAuthParam() { } + + public LoginRequestAuthParam(Location where, String key, String value, Boolean showHeader) { + this.key = key; + this.value = value; + this.where = where; + this.showHeader = showHeader; + } + + @Override + public boolean addAuthTokens(OriginalHttpRequest request) { + if (this.key == null) return false; + return TokenPayloadModifier.tokenPayloadModifier(request, this.key, this.value, this.where); + } + + @Override + public boolean removeAuthTokens(OriginalHttpRequest request) { + if (this.key == null) return false; + return TokenPayloadModifier.tokenPayloadModifier(request, this.key, null, this.where); + } + + @Override + public boolean authTokenPresent(OriginalHttpRequest request) { + if (this.key == null) return false; + String k = this.key.toLowerCase().trim(); + + if (where.toString().equals(AuthParam.Location.BODY.toString())) { + String body = request.getBody(); + BasicDBObject basicDBObject = BasicDBObject.parse(request.getBody()); + BasicDBObject data = JSONUtils.flattenWithDots(basicDBObject); + return data.keySet().contains(this.key); + } else { + Map> headers = request.getHeaders(); + return headers.containsKey(k); + } + } + + public Location getWhere() { + return where; + } + + public void setWhere(Location where) { + this.where = where; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Boolean getShowHeader() { + return showHeader; + } + + public void setShowHeader(Boolean showHeader) { + this.showHeader = showHeader; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/LoginVerificationCodeData.java b/libs/dao/src/main/java/com/akto/dto/testing/LoginVerificationCodeData.java new file mode 100644 index 0000000000..d0ab965fb3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/LoginVerificationCodeData.java @@ -0,0 +1,30 @@ +package com.akto.dto.testing; + +public class LoginVerificationCodeData { + + private String key; + + private String regexString; + + public LoginVerificationCodeData() { } + public LoginVerificationCodeData(String key, String regexString) { + this.key = key; + this.regexString = regexString; + } + + public String getKey() { + return this.key; + } + + public String getRegexString() { + return this.regexString; + } + + public void setKey(String key) { + this.key = key; + } + + public void setRegexString(String regexString) { + this.regexString = regexString; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/LoginWorkflowGraphEdge.java b/libs/dao/src/main/java/com/akto/dto/testing/LoginWorkflowGraphEdge.java new file mode 100644 index 0000000000..1b2adfe827 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/LoginWorkflowGraphEdge.java @@ -0,0 +1,52 @@ +package com.akto.dto.testing; + +public class LoginWorkflowGraphEdge { + private String source; + + private String target; + + private String id; + + public LoginWorkflowGraphEdge() { + } + + public LoginWorkflowGraphEdge(String source, String target, String id) { + this.source = source; + this.target = target; + this.id = id; + } + + public String getSource() { + return this.source; + } + + public String getTarget() { + return this.target; + } + + public String getId() { + return this.id; + } + + public void setSource(String source) { + this.source = source; + } + + public void setTarget(String target) { + this.target = target; + } + + public void setId(String id) { + this.id = id; + } + + + @Override + public String toString() { + return "{" + + "\"source\":\"" + getSource() + "\"" + + ",\"target\":\"" + getTarget() + "\"" + + ",\"id\":\"" + getId() + "\"" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/OtpTestData.java b/libs/dao/src/main/java/com/akto/dto/testing/OtpTestData.java new file mode 100644 index 0000000000..52a71101fa --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/OtpTestData.java @@ -0,0 +1,42 @@ +package com.akto.dto.testing; + +import java.util.UUID; +public class OtpTestData { + + private String uuid; + + private String otpText; + + private int createdAtEpoch; + + public OtpTestData() { } + public OtpTestData(String uuid, String otpText, int createdAtEpoch) { + this.uuid = uuid; + this.otpText = otpText; + this.createdAtEpoch = createdAtEpoch; + } + + public String getUuid() { + return this.uuid; + } + + public String getOtpText() { + return this.otpText; + } + + public int getCreatedAtEpoch() { + return this.createdAtEpoch; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setOtpText(String otpText) { + this.otpText = otpText; + } + + public void setCreatedAtEpoch(int createdAtEpoch) { + this.createdAtEpoch = createdAtEpoch; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/RequestData.java b/libs/dao/src/main/java/com/akto/dto/testing/RequestData.java new file mode 100644 index 0000000000..6e8325dd20 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/RequestData.java @@ -0,0 +1,105 @@ +package com.akto.dto.testing; + +public class RequestData { + + private String body; + + private String headers; + + private String queryParams; + + private String url; + + private String method; + + private String type; + + private String regex; + + private String otpRefUuid; + + private String tokenFetchCommand; + + public RequestData() { } + public RequestData(String body, String headers, String queryParams, String url, String method, String type, String regex, String otpRefUuid, String tokenFetchCommand) { + this.body = body; + this.headers = headers; + this.queryParams = queryParams; + this.url = url; + this.method = method; + this.type = type; + this.regex = regex; + this.otpRefUuid = otpRefUuid; + this.tokenFetchCommand = tokenFetchCommand; + } + + public String getBody() { + return this.body; + } + + public String getHeaders() { + return this.headers; + } + + public String getQueryParams() { + return this.queryParams; + } + + public String getUrl() { + return this.url; + } + + public String getMethod() { + return this.method; + } + + public String getType() { + return this.type; + } + + public String getRegex() { + return this.regex; + } + + public String getOtpRefUuid() { + return this.otpRefUuid; + } + + public String getTokenFetchCommand() { + return this.tokenFetchCommand; + } + + public void setBody(String body) { + this.body = body; + } + + public void setHeaders(String headers) { + this.headers = headers; + } + + public void setQueryParams(String queryParams) { + this.queryParams = queryParams; + } + public void setUrl(String url) { + this.url = url; + } + public void setMethod(String method) { + this.method = method; + } + + public void setType(String type) { + this.type = type; + } + + public void setRegex(String regex) { + this.regex = regex; + } + + public void setOtpRefUuid(String otpRefUuid) { + this.otpRefUuid = otpRefUuid; + } + + public void setTokenFetchCommand(String tokenFetchCommand) { + this.tokenFetchCommand = tokenFetchCommand; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestResult.java b/libs/dao/src/main/java/com/akto/dto/testing/TestResult.java new file mode 100644 index 0000000000..285e21f5c9 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestResult.java @@ -0,0 +1,96 @@ +package com.akto.dto.testing; + +import com.akto.dto.testing.info.TestInfo; + +import java.util.List; + +public class TestResult extends GenericTestResult { + + private String message; + private List errors; + + private String originalMessage; + private double percentageMatch; + private TestInfo testInfo; + + public enum Confidence { + HIGH, MEDIUM, LOW + } + + public enum TestError { + NO_PATH("No sample data found for the API"), + NO_MESSAGE_WITH_AUTH_TOKEN("No sample data found for the API which contains the auth token"), + NO_AUTH_MECHANISM("No authentication mechanism saved"), + API_REQUEST_FAILED("API request failed"), + SOMETHING_WENT_WRONG("OOPS! Something went wrong"), + FAILED_TO_CONVERT_TEST_REQUEST_TO_STRING("Failed to store test"), + INSUFFICIENT_MESSAGES("Insufficient messages"), + NO_AUTH_TOKEN_FOUND("No authentication token found"), + FAILED_DOWNLOADING_NUCLEI_TEMPLATE("Failed downloading nuclei template"), + FAILED_DOWNLOADING_PAYLOAD_FILES("Failed downloading payload files"), + FAILED_BUILDING_NUCLEI_TEMPLATE("Failed building nuclei template"); + + private final String message; + + TestError(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + } + + public TestResult(String message, String originalMessage, List errors, double percentageMatch, boolean isVulnerable, + Confidence confidence, TestInfo testInfo) { + super(isVulnerable, confidence); + this.message = message; + this.errors = errors; + this.originalMessage = originalMessage; + this.percentageMatch = percentageMatch; + this.testInfo = testInfo; + } + + public TestResult() { + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public List getErrors() { + return errors; + } + + public void setErrors(List errors) { + this.errors = errors; + } + + public String getOriginalMessage() { + return originalMessage; + } + + public void setOriginalMessage(String originalMessage) { + this.originalMessage = originalMessage; + } + + public double getPercentageMatch() { + return percentageMatch; + } + + public void setPercentageMatch(double percentageMatch) { + this.percentageMatch = !Double.isFinite(percentageMatch) ? 0 : percentageMatch; + } + + public TestInfo getTestInfo() { + return testInfo; + } + + public void setTestInfo(TestInfo testInfo) { + this.testInfo = testInfo; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestRoles.java b/libs/dao/src/main/java/com/akto/dto/testing/TestRoles.java new file mode 100644 index 0000000000..06e102eacb --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestRoles.java @@ -0,0 +1,101 @@ +package com.akto.dto.testing; + +import com.akto.dao.testing.EndpointLogicalGroupDao; +import com.mongodb.client.model.Filters; +import org.bson.codecs.pojo.annotations.BsonIgnore; +import org.bson.types.ObjectId; + +import static com.akto.util.Constants.ID; + +public class TestRoles { + private ObjectId id; + public static final String NAME = "name"; + private String name; + private ObjectId endpointLogicalGroupId; + private AuthMechanism authMechanism; + @BsonIgnore + private EndpointLogicalGroup endpointLogicalGroup; + private String createdBy; + private int createdTs; + public static final String LAST_UPDATED_TS = "lastUpdatedTs"; + private int lastUpdatedTs; + public TestRoles(){} + public TestRoles(ObjectId id, String name, ObjectId endpointLogicalGroupId, AuthMechanism authMechanism, String createdBy, int createdTs, int lastUpdatedTs) { + this.id = id; + this.name = name; + this.endpointLogicalGroupId = endpointLogicalGroupId; + this.authMechanism = authMechanism; + this.createdBy = createdBy; + this.createdTs = createdTs; + this.lastUpdatedTs = lastUpdatedTs; + } + public EndpointLogicalGroup fetchEndpointLogicalGroup() { + if (this.endpointLogicalGroup == null) { + this.endpointLogicalGroup = EndpointLogicalGroupDao.instance.findOne(Filters.eq(ID, this.endpointLogicalGroupId)); + } + return this.endpointLogicalGroup; + } + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ObjectId getEndpointLogicalGroupId() { + return endpointLogicalGroupId; + } + + public void setEndpointLogicalGroupId(ObjectId endpointLogicalGroupId) { + this.endpointLogicalGroupId = endpointLogicalGroupId; + } + + public AuthMechanism getAuthMechanism() { + return authMechanism; + } + + public void setAuthMechanism(AuthMechanism authMechanism) { + this.authMechanism = authMechanism; + } + + public EndpointLogicalGroup getEndpointLogicalGroup() { + return endpointLogicalGroup; + } + + public void setEndpointLogicalGroup(EndpointLogicalGroup endpointLogicalGroup) { + this.endpointLogicalGroup = endpointLogicalGroup; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public int getCreatedTs() { + return createdTs; + } + + public void setCreatedTs(int createdTs) { + this.createdTs = createdTs; + } + + public int getLastUpdatedTs() { + return lastUpdatedTs; + } + + public void setLastUpdatedTs(int lastUpdatedTs) { + this.lastUpdatedTs = lastUpdatedTs; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestingEndpoints.java b/libs/dao/src/main/java/com/akto/dto/testing/TestingEndpoints.java new file mode 100644 index 0000000000..46e37464ec --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestingEndpoints.java @@ -0,0 +1,30 @@ +package com.akto.dto.testing; + +import com.akto.dto.ApiInfo; + +import java.util.List; + +public abstract class TestingEndpoints { + private Type type; + + public TestingEndpoints(Type type) { + this.type = type; + } + + public abstract List returnApis(); + + public abstract boolean containsApi (ApiInfo.ApiInfoKey key); + + + public enum Type { + CUSTOM, COLLECTION_WISE, WORKFLOW, LOGICAL_GROUP + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java b/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java new file mode 100644 index 0000000000..45787d7769 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestingRun.java @@ -0,0 +1,190 @@ +package com.akto.dto.testing; + +import org.bson.codecs.pojo.annotations.BsonIgnore; +import org.bson.types.ObjectId; + +public class TestingRun { + + private ObjectId id; + public static final String SCHEDULE_TIMESTAMP = "scheduleTimestamp"; + private int scheduleTimestamp; + public static final String PICKED_UP_TIMESTAMP = "pickedUpTimestamp"; + private int pickedUpTimestamp; + public static final String END_TIMESTAMP = "endTimestamp"; + private int endTimestamp; + public static final String STATE = "state"; + private State state; + private String userEmail; + public static final String _TESTING_ENDPOINTS = "testingEndpoints"; + private TestingEndpoints testingEndpoints; + private int testIdConfig; + private int periodInSeconds; + private int testRunTime; + private int maxConcurrentRequests; + + @BsonIgnore + private String hexId; + @BsonIgnore + private TestingRunConfig testingRunConfig; + + private String name; + + public TestingRun() { } + + public TestingRun(int scheduleTimestamp, String userEmail, TestingEndpoints testingEndpoints, int testIdConfig, State state, int periodInSeconds, String name) { + this.scheduleTimestamp = scheduleTimestamp; + this.testRunTime = -1; + this.maxConcurrentRequests = -1; + this.endTimestamp = -1; + this.pickedUpTimestamp = -1; + this.userEmail = userEmail; + this.testingEndpoints = testingEndpoints; + this.testIdConfig = testIdConfig; + this.state = state; + this.periodInSeconds = periodInSeconds; + this.name = name; + } + public TestingRun(int scheduleTimestamp, String userEmail, TestingEndpoints testingEndpoints, int testIdConfig, State state, int periodInSeconds, String name, int testRunTime, int maxConcurrentRequests) { + this.scheduleTimestamp = scheduleTimestamp; + this.testRunTime = testRunTime; + this.maxConcurrentRequests = maxConcurrentRequests; + this.endTimestamp = -1; + this.pickedUpTimestamp = -1; + this.userEmail = userEmail; + this.testingEndpoints = testingEndpoints; + this.testIdConfig = testIdConfig; + this.state = state; + this.periodInSeconds = periodInSeconds; + this.name = name; + } + + public TestingRunConfig getTestingRunConfig() { + return testingRunConfig; + } + + public void setTestingRunConfig(TestingRunConfig testingRunConfig) { + this.testingRunConfig = testingRunConfig; + } + + public int getTestRunTime() { + return testRunTime; + } + + public void setTestRunTime(int testRunTime) { + this.testRunTime = testRunTime; + } + + public int getMaxConcurrentRequests() { + return maxConcurrentRequests; + } + + public void setMaxConcurrentRequests(int maxConcurrentRequests) { + this.maxConcurrentRequests = maxConcurrentRequests; + } + + // if u r adding anything here make sure to add to stopAllTests() method too + public enum State { + SCHEDULED, RUNNING, STOPPED, COMPLETED + } + + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public int getScheduleTimestamp() { + return scheduleTimestamp; + } + + public void setScheduleTimestamp(int scheduleTimestamp) { + this.scheduleTimestamp = scheduleTimestamp; + } + + public int getEndTimestamp() { + return endTimestamp; + } + + public void setEndTimestamp(int endTimestamp) { + this.endTimestamp = endTimestamp; + } + + public String getUserEmail() { + return userEmail; + } + + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + + public TestingEndpoints getTestingEndpoints() { + return testingEndpoints; + } + + public void setTestingEndpoints(TestingEndpoints testingEndpoints) { + this.testingEndpoints = testingEndpoints; + } + + public int getTestIdConfig() { + return testIdConfig; + } + + public void setTestIdConfig(int testIdConfig) { + this.testIdConfig = testIdConfig; + } + + public int getPickedUpTimestamp() { + return pickedUpTimestamp; + } + + public void setPickedUpTimestamp(int pickedUpTimestamp) { + this.pickedUpTimestamp = pickedUpTimestamp; + } + + public int getPeriodInSeconds() { + return this.periodInSeconds; + } + + public void setPeriodInSeconds(int periodInSeconds) { + this.periodInSeconds = periodInSeconds; + } + + public State getState() { + return this.state; + } + + public void setState(State state) { + this.state = state; + } + + public String getHexId() { + return this.id.toHexString(); + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", scheduleTimestamp='" + getScheduleTimestamp() + "'" + + ", pickedUpTimestamp='" + getPickedUpTimestamp() + "'" + + ", endTimestamp='" + getEndTimestamp() + "'" + + ", state='" + getState() + "'" + + ", userEmail='" + getUserEmail() + "'" + + ", testingEndpoints='" + getTestingEndpoints() + "'" + + ", testIdConfig='" + getTestIdConfig() + "'" + + ", periodInSeconds='" + getPeriodInSeconds() + "'" + + ", name='" + getName() + "'" + + "}"; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestingRunConfig.java b/libs/dao/src/main/java/com/akto/dto/testing/TestingRunConfig.java new file mode 100644 index 0000000000..338d4a0cd6 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestingRunConfig.java @@ -0,0 +1,62 @@ +package com.akto.dto.testing; + +import com.akto.dto.ApiInfo; +import org.bson.codecs.pojo.annotations.BsonId; +import org.bson.types.ObjectId; + +import java.util.List; +import java.util.Map; + +import static com.akto.util.enums.GlobalEnums.*; + +public class TestingRunConfig { + + @BsonId + private int id; + private Map> collectionWiseApiInfoKey; + private List testSubCategoryList; + private ObjectId authMechanismId; + + public TestingRunConfig() {} + public TestingRunConfig(int id, Map> collectionWiseApiInfoKey, + List testSubCategoryList, + ObjectId authMechanismId) { + + this.id = id; + this.collectionWiseApiInfoKey = collectionWiseApiInfoKey; + this.testSubCategoryList = testSubCategoryList; + this.authMechanismId = authMechanismId; + } + + public List getTestSubCategoryList() { + return testSubCategoryList; + } + + public void setTestSubCategoryList(List testSubCategoryList) { + this.testSubCategoryList = testSubCategoryList; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Map> getCollectionWiseApiInfoKey() { + return collectionWiseApiInfoKey; + } + + public void setCollectionWiseApiInfoKey(Map> collectionWiseApiInfoKey) { + this.collectionWiseApiInfoKey = collectionWiseApiInfoKey; + } + + public ObjectId getAuthMechanismId() { + return authMechanismId; + } + + public void setAuthMechanismId(ObjectId authMechanismId) { + this.authMechanismId = authMechanismId; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResult.java b/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResult.java new file mode 100644 index 0000000000..81ffa1f2f0 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResult.java @@ -0,0 +1,178 @@ +package com.akto.dto.testing; + +import com.akto.dto.ApiInfo; +import com.akto.dto.type.SingleTypeInfo; +import org.bson.codecs.pojo.annotations.BsonIgnore; +import org.bson.types.ObjectId; + +import java.util.List; + +public class TestingRunResult { + private ObjectId id; + @BsonIgnore + private String hexId; + + public static final String TEST_RUN_ID = "testRunId"; + private ObjectId testRunId; + public static final String API_INFO_KEY = "apiInfoKey"; + private ApiInfo.ApiInfoKey apiInfoKey; + public static final String TEST_SUPER_TYPE = "testSuperType"; + private String testSuperType; + public static final String TEST_SUB_TYPE = "testSubType"; + private String testSubType; + public static final String TEST_RESULTS = "testResults"; + private List testResults; + public static final String VULNERABLE = "vulnerable"; + private boolean vulnerable; + public static final String SINGLE_TYPE_INFOS = "singleTypeInfos"; + private List singleTypeInfos; + public static final String CONFIDENCE_PERCENTAGE = "confidencePercentage"; + private int confidencePercentage; + + public static final String START_TIMESTAMP = "startTimestamp"; + private int startTimestamp; + public static final String END_TIMESTAMP = "endTimestamp"; + private int endTimestamp; + public static final String TEST_RUN_RESULT_SUMMARY_ID = "testRunResultSummaryId"; + private ObjectId testRunResultSummaryId; + + public TestingRunResult() { } + + public TestingRunResult(ObjectId testRunId, ApiInfo.ApiInfoKey apiInfoKey, String testSuperType, String testSubType, + List testResults, boolean vulnerable, List singleTypeInfos, + int confidencePercentage, int startTimestamp, int endTimestamp, ObjectId testRunResultSummaryId) { + this.testRunId = testRunId; + this.apiInfoKey = apiInfoKey; + this.testSuperType = testSuperType; + this.testSubType = testSubType; + this.testResults = testResults; + this.vulnerable = vulnerable; + this.singleTypeInfos = singleTypeInfos; + this.confidencePercentage = confidencePercentage; + this.startTimestamp = startTimestamp; + this.endTimestamp = endTimestamp; + this.testRunResultSummaryId = testRunResultSummaryId; + } + + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public ApiInfo.ApiInfoKey getApiInfoKey() { + return apiInfoKey; + } + + public void setApiInfoKey(ApiInfo.ApiInfoKey apiInfoKey) { + this.apiInfoKey = apiInfoKey; + } + + public ObjectId getTestRunId() { + return testRunId; + } + + public void setTestRunId(ObjectId testRunId) { + this.testRunId = testRunId; + } + + + public String getHexId() { + if (hexId == null) return this.id.toHexString(); + return this.hexId; + } + + public void setHexId(String hexId) { + this.hexId = hexId; + } + + public int getStartTimestamp() { + return this.startTimestamp; + } + + public void setStartTimestamp(int startTimestamp) { + this.startTimestamp = startTimestamp; + } + + public int getEndTimestamp() { + return this.endTimestamp; + } + + public void setEndTimestamp(int endTimestamp) { + this.endTimestamp = endTimestamp; + } + + public ObjectId getTestRunResultSummaryId() { + return this.testRunResultSummaryId; + } + + public void setTestRunResultSummaryId(ObjectId testRunResultSummaryId) { + this.testRunResultSummaryId = testRunResultSummaryId; + } + + public String getTestSuperType() { + return testSuperType; + } + + public void setTestSuperType(String testSuperType) { + this.testSuperType = testSuperType; + } + + public String getTestSubType() { + return testSubType; + } + + public void setTestSubType(String testSubType) { + this.testSubType = testSubType; + } + + public List getTestResults() { + return testResults; + } + + public void setTestResults(List testResults) { + this.testResults = testResults; + } + + public boolean isVulnerable() { + return vulnerable; + } + + public void setVulnerable(boolean vulnerable) { + this.vulnerable = vulnerable; + } + + public List getSingleTypeInfos() { + return singleTypeInfos; + } + + public void setSingleTypeInfos(List singleTypeInfos) { + this.singleTypeInfos = singleTypeInfos; + } + + public int getConfidencePercentage() { + return confidencePercentage; + } + + public void setConfidencePercentage(int confidencePercentage) { + this.confidencePercentage = confidencePercentage; + } + + @Override + public String toString() { + return "TestingRunResult{" + + "id=" + id + + ", testRunId=" + testRunId + + ", apiInfoKey=" + apiInfoKey + + ", testSuperType='" + testSuperType + '\'' + + ", testSubType='" + testSubType + '\'' + + ", isVulnerable=" + vulnerable + + ", confidencePercentage=" + confidencePercentage + + ", startTimestamp=" + startTimestamp + + ", endTimestamp=" + endTimestamp + + ", testRunResultSummaryId=" + testRunResultSummaryId + + '}'; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java b/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java new file mode 100644 index 0000000000..8852c018c1 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestingRunResultSummary.java @@ -0,0 +1,136 @@ +package com.akto.dto.testing; + +import java.util.Map; + +import org.bson.codecs.pojo.annotations.BsonIgnore; +import org.bson.types.ObjectId; + +public class TestingRunResultSummary { + + public static final String START_TIMESTAMP = "startTimestamp"; + public static final String END_TIMESTAMP = "endTimestamp"; + public static final String COUNT_ISSUES = "countIssues"; + public static final String TOTAL_APIS = "totalApis"; + public static final String TESTING_RUN_ID = "testingRunId"; + public static final String STATE = "state"; + public static final String TEST_RESULTS_COUNT = "testResultsCount"; + + private ObjectId id; + private int startTimestamp; + private int endTimestamp; + private Map countIssues; + private int totalApis; + private ObjectId testingRunId; + @BsonIgnore + private String testingRunHexId; + + private TestingRun.State state; + private int testResultsCount; + + @BsonIgnore + private String hexId; + + + public TestingRunResultSummary() { + } + + public TestingRunResultSummary(int startTimestamp, int endTimestamp, Map countIssues, int totalApis, ObjectId testingRunId, String testingRunHexId, int testResultsCount) { + this.startTimestamp = startTimestamp; + this.endTimestamp = endTimestamp; + this.countIssues = countIssues; + this.totalApis = totalApis; + this.testingRunId = testingRunId; + this.testingRunHexId = testingRunHexId; + this.state = TestingRun.State.RUNNING; + this.testResultsCount = testResultsCount; + } + + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public int getStartTimestamp() { + return this.startTimestamp; + } + + public void setStartTimestamp(int startTimestamp) { + this.startTimestamp = startTimestamp; + } + + public int getEndTimestamp() { + return this.endTimestamp; + } + + public void setEndTimestamp(int endTimestamp) { + this.endTimestamp = endTimestamp; + } + + public Map getCountIssues() { + return this.countIssues; + } + + public void setCountIssues(Map countIssues) { + this.countIssues = countIssues; + } + + public int getTotalApis() { + return this.totalApis; + } + + public void setTotalApis(int totalApis) { + this.totalApis = totalApis; + } + + public ObjectId getTestingRunId() { + return this.testingRunId; + } + + public void setTestingRunId(ObjectId testingRunId) { + this.testingRunId = testingRunId; + } + + public String getTestingRunHexId() { + return this.testingRunHexId; + } + + public void setTestingRunHexId(String testingRunHexId) { + this.testingRunHexId = testingRunHexId; + } + + public TestingRun.State getState() { + return this.state; + } + + public void setState(TestingRun.State state) { + this.state = state; + } + + public String getHexId() { + return this.id.toHexString(); + } + + public int getTestResultsCount() { + return testResultsCount; + } + + public void setTestResultsCount(int testResultsCount) { + this.testResultsCount = testResultsCount; + } + + @Override + public String toString() { + return "{" + + " startTimestamp='" + getStartTimestamp() + "'" + + ", endTimestamp='" + getEndTimestamp() + "'" + + ", countIssues='" + getCountIssues() + "'" + + ", totalApis='" + getTotalApis() + "'" + + ", testingRunId='" + getTestingRunId() + "'" + + ", testingRunHexId='" + getTestingRunHexId() + "'" + + ", state='" + getState() + "'" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/TestingSchedule.java b/libs/dao/src/main/java/com/akto/dto/testing/TestingSchedule.java new file mode 100644 index 0000000000..c69628e306 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/TestingSchedule.java @@ -0,0 +1,117 @@ +package com.akto.dto.testing; + +import org.bson.codecs.pojo.annotations.BsonId; + +public class TestingSchedule { + + String authorEmail; + int creationTimestamp; + + String lastEditorEmail; + int lastUpdateTimestamp; + + @BsonId + int id; + + public static final String START_TIMESTAMP = "startTimestamp"; + int startTimestamp; + boolean recurring; + TestingRun sampleTestingRun; + + public TestingSchedule() { + } + + public TestingSchedule(String authorEmail, int creationTimestamp, String lastEditorEmail, int lastUpdateTimestamp, int id, int startTimestamp, boolean recurring, TestingRun sampleTestingRun) { + this.authorEmail = authorEmail; + this.creationTimestamp = creationTimestamp; + this.lastEditorEmail = lastEditorEmail; + this.lastUpdateTimestamp = lastUpdateTimestamp; + this.id = id; + this.startTimestamp = startTimestamp; + this.recurring = recurring; + this.sampleTestingRun = sampleTestingRun; +} + + public String getAuthorEmail() { + return this.authorEmail; + } + + public void setAuthorEmail(String authorEmail) { + this.authorEmail = authorEmail; + } + + public int getCreationTimestamp() { + return this.creationTimestamp; + } + + public void setCreationTimestamp(int creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public String getLastEditorEmail() { + return this.lastEditorEmail; + } + + public void setLastEditorEmail(String lastEditorEmail) { + this.lastEditorEmail = lastEditorEmail; + } + + public int getLastUpdateTimestamp() { + return this.lastUpdateTimestamp; + } + + public void setLastUpdateTimestamp(int lastUpdateTimestamp) { + this.lastUpdateTimestamp = lastUpdateTimestamp; + } + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + public int getStartTimestamp() { + return this.startTimestamp; + } + + public void setStartTimestamp(int startTimestamp) { + this.startTimestamp = startTimestamp; + } + + public boolean isRecurring() { + return this.recurring; + } + + public boolean getRecurring() { + return this.recurring; + } + + public void setRecurring(boolean recurring) { + this.recurring = recurring; + } + + public TestingRun getSampleTestingRun() { + return this.sampleTestingRun; + } + + public void setSampleTestingRun(TestingRun sampleTestingRun) { + this.sampleTestingRun = sampleTestingRun; + } + + @Override + public String toString() { + return "{" + + " authorEmail='" + getAuthorEmail() + "'" + + ", creationTimestamp='" + getCreationTimestamp() + "'" + + ", lastEditorEmail='" + getLastEditorEmail() + "'" + + ", lastUpdateTimestamp='" + getLastUpdateTimestamp() + "'" + + ", id='" + getId() + "'" + + ", startTimestamp='" + getStartTimestamp() + "'" + + ", recurring='" + isRecurring() + "'" + + ", sampleTestingRun='" + getSampleTestingRun() + "'" + + "}"; + } + +} \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dto/testing/WorkflowNodeDetails.java b/libs/dao/src/main/java/com/akto/dto/testing/WorkflowNodeDetails.java new file mode 100644 index 0000000000..b3e219c4f9 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/WorkflowNodeDetails.java @@ -0,0 +1,171 @@ +package com.akto.dto.testing; + +import com.akto.dto.type.URLMethods.Method; + +public class WorkflowNodeDetails { + int apiCollectionId; + String endpoint; + Method method; + WorkflowUpdatedSampleData updatedSampleData; + + Type type = Type.API; + boolean overrideRedirect; + String testValidatorCode; + + int waitInSeconds; + + int maxPollRetries; + + int pollRetryDuration; + + String otpRegex; + + String otpRefUuid; + + public enum Type { + POLL, API, OTP, RECORDED + } + + // call this function to see if data being passed is legit or not + public String validate() { + if (this.type == null ) return "Type can't be null"; + if (this.endpoint == null ) return "URL can't be null"; + if (this.method == null ) return "Method can't be null"; + int waitThreshold = 60; + if (waitInSeconds > waitThreshold) return "Wait time should be <= " + waitThreshold; + + return null; + } + + public WorkflowNodeDetails() { + } + + public WorkflowNodeDetails(int apiCollectionId, String endpoint, Method method, String testValidatorCode, + WorkflowUpdatedSampleData updatedSampleData, Type type, boolean overrideRedirect, + int waitInSeconds, int maxPollRetries, int pollRetryDuration, String otpRegex, String otpRefUuid) { + this.apiCollectionId = apiCollectionId; + this.endpoint = endpoint; + this.method = method; + this.updatedSampleData = updatedSampleData; + this.type = type; + this.overrideRedirect = overrideRedirect; + this.testValidatorCode = testValidatorCode; + this.waitInSeconds = waitInSeconds; + this.maxPollRetries = maxPollRetries; + this.pollRetryDuration = pollRetryDuration; + this.otpRegex = otpRegex; + this.otpRefUuid = otpRefUuid; + } + + public int getApiCollectionId() { + return this.apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public String getEndpoint() { + return this.endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public Method getMethod() { + return this.method; + } + + public void setMethod(Method method) { + this.method = method; + } + + public WorkflowUpdatedSampleData getUpdatedSampleData() { + return this.updatedSampleData; + } + + public void setUpdatedSampleData(WorkflowUpdatedSampleData updatedSampleData) { + this.updatedSampleData = updatedSampleData; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + + public boolean getOverrideRedirect() { + return overrideRedirect; + } + + public void setOverrideRedirect(boolean overrideRedirect) { + this.overrideRedirect = overrideRedirect; + } + + public boolean isOverrideRedirect() { + return overrideRedirect; + } + + public String getTestValidatorCode() { + return testValidatorCode; + } + + public void setTestValidatorCode(String testValidatorCode) { + this.testValidatorCode = testValidatorCode; + } + + public int getWaitInSeconds() { + return waitInSeconds; + } + + public void setWaitInSeconds(int waitInSeconds) { + this.waitInSeconds = waitInSeconds; + } + + public int getMaxPollRetries() { + return maxPollRetries; + } + + public void setMaxPollRetries(int maxPollRetries) { + this.maxPollRetries = maxPollRetries; + } + + public int getPollRetryDuration() { + return pollRetryDuration; + } + + public void setPollRetryDuration(int pollRetryDuration) { + this.pollRetryDuration = pollRetryDuration; + } + + public String getOtpRegex() { + return otpRegex; + } + + public void setOtpRegex(String otpRegex) { + this.otpRegex = otpRegex; + } + + public String getOtpRefUuid() { + return otpRefUuid; + } + + public void setOtpRefUuid(String otpRefUuid) { + this.otpRefUuid = otpRefUuid; + } + + @Override + public String toString() { + return "{" + + " apiCollectionId='" + getApiCollectionId() + "'" + + ", endpoint='" + getEndpoint() + "'" + + ", method='" + getMethod() + "'" + + ", updatedSampleData='" + getUpdatedSampleData() + "'" + + "}"; + } + +} \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dto/testing/WorkflowTest.java b/libs/dao/src/main/java/com/akto/dto/testing/WorkflowTest.java new file mode 100644 index 0000000000..7177dd8371 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/WorkflowTest.java @@ -0,0 +1,141 @@ +package com.akto.dto.testing; + +import java.util.List; +import java.util.Map; + +import org.bson.codecs.pojo.annotations.BsonId; + +public class WorkflowTest { + + public enum State { + DRAFT, COMPLETE, STOPPED; + } + + @BsonId + int id; + + int apiCollectionId; + String author; + int createdTimestamp; + + String editor; + int lastEdited; + + List nodes; + List edges; + + Map mapNodeIdToWorkflowNodeDetails; + State state; + + public WorkflowTest() {} + + public WorkflowTest(int id, int apiCollectionId, String author, int createdTimestamp, String editor, int lastEdited, List nodes, List edges, Map mapNodeIdToWorkflowNodeDetails, State state) { + this.id = id; + this.apiCollectionId = apiCollectionId; + this.author = author; + this.createdTimestamp = createdTimestamp; + this.editor = editor; + this.lastEdited = lastEdited; + this.nodes = nodes; + this.edges = edges; + this.mapNodeIdToWorkflowNodeDetails = mapNodeIdToWorkflowNodeDetails; + this.state = state; + } + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + public int getApiCollectionId() { + return this.apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public String getAuthor() { + return this.author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public int getCreatedTimestamp() { + return this.createdTimestamp; + } + + public void setCreatedTimestamp(int createdTimestamp) { + this.createdTimestamp = createdTimestamp; + } + + public String getEditor() { + return this.editor; + } + + public void setEditor(String editor) { + this.editor = editor; + } + + public int getLastEdited() { + return this.lastEdited; + } + + public void setLastEdited(int lastEdited) { + this.lastEdited = lastEdited; + } + + public List getNodes() { + return this.nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public List getEdges() { + return this.edges; + } + + public void setEdges(List edges) { + this.edges = edges; + } + + public Map getMapNodeIdToWorkflowNodeDetails() { + return this.mapNodeIdToWorkflowNodeDetails; + } + + public void setMapNodeIdToWorkflowNodeDetails(Map mapNodeIdToWorkflowNodeDetails) { + this.mapNodeIdToWorkflowNodeDetails = mapNodeIdToWorkflowNodeDetails; + } + + public State getState() { + return this.state; + } + + public void setState(State state) { + this.state = state; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", apiCollectionId='" + getApiCollectionId() + "'" + + ", author='" + getAuthor() + "'" + + ", createdTimestamp='" + getCreatedTimestamp() + "'" + + ", editor='" + getEditor() + "'" + + ", lastEdited='" + getLastEdited() + "'" + + ", nodes='" + getNodes() + "'" + + ", edges='" + getEdges() + "'" + + ", mapNodeIdToWorkflowNodeDetails='" + getMapNodeIdToWorkflowNodeDetails() + "'" + + ", state='" + getState() + "'" + + "}"; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/WorkflowTestResult.java b/libs/dao/src/main/java/com/akto/dto/testing/WorkflowTestResult.java new file mode 100644 index 0000000000..cae8b832ab --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/WorkflowTestResult.java @@ -0,0 +1,111 @@ +package com.akto.dto.testing; + +import org.bson.types.ObjectId; + +import java.util.*; + +public class WorkflowTestResult { + private int id; + private int workflowTestId; + public static final String _TEST_RUN_ID = "testRunId"; + private ObjectId testRunId; + public static final String TESTING_RUN_RESULT_SUMMARY_ID = "testingRunResultSummaryId"; + + private ObjectId testingRunResultSummaryId; + Map nodeResultMap; + + public static class NodeResult { + private String message; + private boolean vulnerable; + private List errors; + + public NodeResult() { } + + public NodeResult(String message, boolean vulnerable, List errors) { + this.message = message; + this.vulnerable = vulnerable; + this.errors = errors; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isVulnerable() { + return vulnerable; + } + + public void setVulnerable(boolean vulnerable) { + this.vulnerable = vulnerable; + } + + public List getErrors() { + return errors; + } + + public void setErrors(List errors) { + this.errors = errors; + } + } + + public WorkflowTestResult() { + } + + public WorkflowTestResult(int id, int workflowTestId, Map nodeResultMap, ObjectId testRunId, ObjectId testingRunResultSummaryId) { + this.id = id; + this.workflowTestId = workflowTestId; + this.nodeResultMap = nodeResultMap; + this.testRunId = testRunId; + this.testingRunResultSummaryId = testingRunResultSummaryId; + } + + public void addNodeResult(String nodeId, String message, List testErrors) { + if (this.nodeResultMap == null) this.nodeResultMap = new HashMap<>(); + NodeResult nodeResult = new NodeResult(message, false, testErrors); + this.nodeResultMap.put(nodeId, nodeResult); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getWorkflowTestId() { + return workflowTestId; + } + + public void setWorkflowTestId(int workflowTestId) { + this.workflowTestId = workflowTestId; + } + + public Map getNodeResultMap() { + return nodeResultMap; + } + + public void setNodeResultMap(Map nodeResultMap) { + this.nodeResultMap = nodeResultMap; + } + + public ObjectId getTestRunId() { + return testRunId; + } + + public void setTestRunId(ObjectId testRunId) { + this.testRunId = testRunId; + } + + public ObjectId getTestingRunResultSummaryId() { + return testingRunResultSummaryId; + } + + public void setTestingRunResultSummaryId(ObjectId testingRunResultSummaryId) { + this.testingRunResultSummaryId = testingRunResultSummaryId; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/WorkflowTestingEndpoints.java b/libs/dao/src/main/java/com/akto/dto/testing/WorkflowTestingEndpoints.java new file mode 100644 index 0000000000..242e2f32b1 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/WorkflowTestingEndpoints.java @@ -0,0 +1,37 @@ +package com.akto.dto.testing; + +import com.akto.dto.ApiInfo; + +import java.util.List; + +public class WorkflowTestingEndpoints extends TestingEndpoints{ + public static final String _WORK_FLOW_TEST = "workflowTest"; + private WorkflowTest workflowTest; + + public WorkflowTestingEndpoints() { + super(Type.WORKFLOW); + } + + public WorkflowTestingEndpoints(WorkflowTest workflowTest) { + super(Type.WORKFLOW); + this.workflowTest = workflowTest; + } + + @Override + public List returnApis() { + return null; + } + + @Override + public boolean containsApi(ApiInfo.ApiInfoKey key) { + return false; + } + + public WorkflowTest getWorkflowTest() { + return workflowTest; + } + + public void setWorkflowTest(WorkflowTest workflowTest) { + this.workflowTest = workflowTest; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/WorkflowUpdatedSampleData.java b/libs/dao/src/main/java/com/akto/dto/testing/WorkflowUpdatedSampleData.java new file mode 100644 index 0000000000..379758a118 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/WorkflowUpdatedSampleData.java @@ -0,0 +1,70 @@ +package com.akto.dto.testing; + +public class WorkflowUpdatedSampleData { + String orig; + String requestUrl; + String queryParams; + String requestHeaders; + String requestPayload; + + public WorkflowUpdatedSampleData() {} + + public WorkflowUpdatedSampleData(String orig, String queryParams, String requestHeaders, String requestPayload, String requestUrl) { + this.orig = orig; + this.queryParams = queryParams; + this.requestHeaders = requestHeaders; + this.requestPayload = requestPayload; + this.requestUrl = requestUrl; + } + + public String getOrig() { + return this.orig; + } + + public void setOrig(String orig) { + this.orig = orig; + } + + public String getQueryParams() { + return this.queryParams; + } + + public void setQueryParams(String queryParams) { + this.queryParams = queryParams; + } + + public String getRequestHeaders() { + return this.requestHeaders; + } + + public void setRequestHeaders(String requestHeaders) { + this.requestHeaders = requestHeaders; + } + + public String getRequestPayload() { + return this.requestPayload; + } + + public void setRequestPayload(String requestPayload) { + this.requestPayload = requestPayload; + } + + public String getRequestUrl() { + return requestUrl; + } + + public void setRequestUrl(String requestUrl) { + this.requestUrl = requestUrl; + } + + @Override + public String toString() { + return "WorkflowUpdatedSampleData{" + + "orig='" + orig + '\'' + + ", requestUrl='" + requestUrl + '\'' + + ", queryParams='" + queryParams + '\'' + + ", requestHeaders='" + requestHeaders + '\'' + + ", requestPayload='" + requestPayload + '\'' + + '}'; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/info/BFLATestInfo.java b/libs/dao/src/main/java/com/akto/dto/testing/info/BFLATestInfo.java new file mode 100644 index 0000000000..24a58c69d9 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/info/BFLATestInfo.java @@ -0,0 +1,33 @@ +package com.akto.dto.testing.info; + +public class BFLATestInfo extends TestInfo { + + private String attackerRole; + private String victimRole; + + public BFLATestInfo() { + super(); + } + + public BFLATestInfo(String attackerRole, String victimRole) { + super(); + this.attackerRole = attackerRole; + this.victimRole = victimRole; + } + + public String getAttackerRole() { + return attackerRole; + } + + public void setAttackerRole(String attackerRole) { + this.attackerRole = attackerRole; + } + + public String getVictimRole() { + return victimRole; + } + + public void setVictimRole(String victimRole) { + this.victimRole = victimRole; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/info/NucleiTestInfo.java b/libs/dao/src/main/java/com/akto/dto/testing/info/NucleiTestInfo.java new file mode 100644 index 0000000000..da0f3b69d2 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/info/NucleiTestInfo.java @@ -0,0 +1,43 @@ +package com.akto.dto.testing.info; + +public class NucleiTestInfo extends TestInfo { + + private String subcategory; + private String templatePath; + + public NucleiTestInfo() { + super(); + } + + public NucleiTestInfo(String subcategory, String templatePath) { + super(); + this.subcategory = subcategory; + this.templatePath = templatePath; + } + + public String getSubcategory() { + return this.subcategory; + } + + public void setSubcategory(String subcategory) { + this.subcategory = subcategory; + } + + public String getTemplatePath() { + return this.templatePath; + } + + public void setTemplatePath(String templatePath) { + this.templatePath = templatePath; + } + + @Override + public String toString() { + return "{" + + " subcategory='" + getSubcategory() + "'" + + ", templatePath='" + getTemplatePath() + "'" + + "}"; + } + + +} \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dto/testing/info/TestInfo.java b/libs/dao/src/main/java/com/akto/dto/testing/info/TestInfo.java new file mode 100644 index 0000000000..3cdc7deb24 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/info/TestInfo.java @@ -0,0 +1,6 @@ +package com.akto.dto.testing.info; + +public abstract class TestInfo { + + public TestInfo() {} +} diff --git a/libs/dao/src/main/java/com/akto/dto/testing/sources/TestSourceConfig.java b/libs/dao/src/main/java/com/akto/dto/testing/sources/TestSourceConfig.java new file mode 100644 index 0000000000..6a50788dd2 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/testing/sources/TestSourceConfig.java @@ -0,0 +1,125 @@ +package com.akto.dto.testing.sources; + +import com.akto.util.enums.GlobalEnums.Severity; +import com.akto.util.enums.GlobalEnums.TestCategory; + +public class TestSourceConfig { + private String id; + + private TestCategory category; + public static final String CATEGORY = "category"; + + private String subcategory; + public static final String SUBCATEGORY = "subcategory"; + + private Severity severity; + private String description; + + private String creator; + public static final String CREATOR = "creator"; + public static final String DEFAULT = "default"; + + private int addedEpoch; + private int stars; + private int installs; + + public TestSourceConfig() { + } + + public TestSourceConfig(String id, TestCategory category, String subcategory, Severity severity, String description, String creator, int addedEpoch) { + this.id = id; + this.category = category; + this.subcategory = subcategory; + this.severity = severity; + this.description = description; + this.creator = creator; + this.addedEpoch = addedEpoch; + } + + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + public TestCategory getCategory() { + return this.category; + } + + public void setCategory(TestCategory category) { + this.category = category; + } + + public String getSubcategory() { + return this.subcategory; + } + + public void setSubcategory(String subcategory) { + this.subcategory = subcategory; + } + + public Severity getSeverity() { + return this.severity; + } + + public void setSeverity(Severity severity) { + this.severity = severity; + } + + public String getDescription() { + return this.description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getCreator() { + return this.creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public int getAddedEpoch() { + return this.addedEpoch; + } + + public void setAddedEpoch(int addedEpoch) { + this.addedEpoch = addedEpoch; + } + + public int getStars() { + return this.stars; + } + + public void setStars(int stars) { + this.stars = stars; + } + + public int getInstalls() { + return this.installs; + } + + public void setInstalls(int installs) { + this.installs = installs; + } + + @Override + public String toString() { + return "{" + + " sourceURL='" + getId() + "'" + + ", category='" + getCategory() + "'" + + ", subcategory='" + getSubcategory() + "'" + + ", severity='" + getSeverity() + "'" + + ", description='" + getDescription() + "'" + + ", creator='" + getCreator() + "'" + + ", addedEpoch='" + getAddedEpoch() + "'" + + ", stars='" + getStars() + "'" + + ", installs='" + getInstalls() + "'" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/third_party_access/Credential.java b/libs/dao/src/main/java/com/akto/dto/third_party_access/Credential.java new file mode 100644 index 0000000000..85f5cda7e5 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/third_party_access/Credential.java @@ -0,0 +1,57 @@ +package com.akto.dto.third_party_access; + +import com.akto.dao.context.Context; +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +@BsonDiscriminator +public abstract class Credential { + public enum Type { + GOOGLE, MYSQL, SALESFORCE, POSTGRESQL, POSTMAN + } + + private Type type; + private int lastUpdatedTs; + private long expiryDuration; + private String name; + + public Credential() {} + + public Credential(Type type, long expiryDuration, String name) { + this.type = type; + this.lastUpdatedTs = Context.now(); + this.expiryDuration = expiryDuration; + this.name = name; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public int getLastUpdatedTs() { + return lastUpdatedTs; + } + + public void setLastUpdatedTs(int lastUpdatedTs) { + this.lastUpdatedTs = lastUpdatedTs; + } + + public long getExpiryDuration() { + return expiryDuration; + } + + public void setExpiryDuration(long expiryDuration) { + this.expiryDuration = expiryDuration; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/third_party_access/GoogleCredential.java b/libs/dao/src/main/java/com/akto/dto/third_party_access/GoogleCredential.java new file mode 100644 index 0000000000..24251c790e --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/third_party_access/GoogleCredential.java @@ -0,0 +1,39 @@ +package com.akto.dto.third_party_access; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +@BsonDiscriminator +public class GoogleCredential extends Credential { + private String accessToken, refreshToken; + + public GoogleCredential() { + super(); + } + + public GoogleCredential( + String accessToken, + String refreshToken, + long expiryDuration, + String name + ) { + super(Type.GOOGLE, expiryDuration, name); + this.accessToken = accessToken; + this.refreshToken = refreshToken; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/third_party_access/PostmanCredential.java b/libs/dao/src/main/java/com/akto/dto/third_party_access/PostmanCredential.java new file mode 100644 index 0000000000..d38f4ba1a9 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/third_party_access/PostmanCredential.java @@ -0,0 +1,33 @@ +package com.akto.dto.third_party_access; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; + +@BsonDiscriminator +public class PostmanCredential extends Credential { + private String workspaceId; + private String apiKey; + + public PostmanCredential() {} + + public PostmanCredential(String name, String workspaceId, String apiKey) { + super(Type.POSTMAN,-1, name); + this.workspaceId = workspaceId; + this.apiKey = apiKey; + } + + public String getWorkspaceId() { + return workspaceId; + } + + public void setWorkspaceId(String workspaceId) { + this.workspaceId = workspaceId; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/third_party_access/ThirdPartyAccess.java b/libs/dao/src/main/java/com/akto/dto/third_party_access/ThirdPartyAccess.java new file mode 100644 index 0000000000..f7e4f6467f --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/third_party_access/ThirdPartyAccess.java @@ -0,0 +1,60 @@ +package com.akto.dto.third_party_access; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; +import org.bson.types.ObjectId; + +@BsonDiscriminator +public class ThirdPartyAccess { + private ObjectId id; + private int timestamp, owner, status; + Credential credential; + + public ThirdPartyAccess() {} + + public ThirdPartyAccess(int timestamp, int owner, int status, Credential credential) { + this.timestamp = timestamp; + this.owner = owner; + this.status = status; + this.credential = credential; + } + + public int getTimestamp() { + return timestamp; + } + + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + + public int getOwner() { + return owner; + } + + public void setOwner(int owner) { + this.owner = owner; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public Credential getCredential() { + return credential; + } + + public void setCredential(Credential credential) { + this.credential = credential; + } + + public ObjectId getId() { + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/traffic/Key.java b/libs/dao/src/main/java/com/akto/dto/traffic/Key.java new file mode 100644 index 0000000000..5bef451824 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/traffic/Key.java @@ -0,0 +1,78 @@ +package com.akto.dto.traffic; + +import com.akto.dto.type.URLMethods.Method; + +public class Key { + int apiCollectionId; + public String url; + public Method method; + public int responseCode; + int bucketStartEpoch; + int bucketEndEpoch; + + public Key() {} + + public Key(int apiCollectionId, String url, Method method, int responseCode, int bucketStartEpoch, int bucketEndEpoch) { + this.apiCollectionId = apiCollectionId; + this.url = url; + this.method = method; + this.responseCode = responseCode; + this.bucketStartEpoch = bucketStartEpoch; + this.bucketEndEpoch = bucketEndEpoch; + } + + public int getApiCollectionId() { + return this.apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public String getUrl() { + return this.url; + } + + public void setUrl(String url) { + this.url = url; + } + + public Method getMethod() { + return this.method; + } + + public void setMethod(Method method) { + this.method = method; + } + + public int getResponseCode() { + return this.responseCode; + } + + public void setResponseCode(int responseCode) { + this.responseCode = responseCode; + } + + public int getBucketStartEpoch() { + return this.bucketStartEpoch; + } + + public void setBucketStartEpoch(int bucketStartEpoch) { + this.bucketStartEpoch = bucketStartEpoch; + } + + public int getBucketEndEpoch() { + return this.bucketEndEpoch; + } + + public void setBucketEndEpoch(int bucketEndEpoch) { + this.bucketEndEpoch = bucketEndEpoch; + } + + @Override + public String toString() { + return apiCollectionId + " " + url + " " + method + " " + responseCode; + } + +} + diff --git a/libs/dao/src/main/java/com/akto/dto/traffic/SampleData.java b/libs/dao/src/main/java/com/akto/dto/traffic/SampleData.java new file mode 100644 index 0000000000..91a0a30c10 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/traffic/SampleData.java @@ -0,0 +1,43 @@ +package com.akto.dto.traffic; + +import java.util.List; + +public class SampleData { + Key id; + List samples; + + + public SampleData() { + } + + public SampleData(Key id, List samples) { + this.id = id; + this.samples = samples; + } + + public Key getId() { + return this.id; + } + + public void setId(Key id) { + this.id = id; + } + + public List getSamples() { + return this.samples; + } + + public void setSamples(List samples) { + this.samples = samples; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", samples='" + getSamples() + "'" + + "}"; + } + + +} diff --git a/libs/dao/src/main/java/com/akto/dto/traffic/TrafficInfo.java b/libs/dao/src/main/java/com/akto/dto/traffic/TrafficInfo.java new file mode 100644 index 0000000000..6812f07922 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/traffic/TrafficInfo.java @@ -0,0 +1,44 @@ +package com.akto.dto.traffic; + +import java.util.Map; + +import org.bson.codecs.pojo.annotations.BsonId; + +public class TrafficInfo { + + @BsonId + Key id; + public Map mapHoursToCount; + + public TrafficInfo() { + } + + public TrafficInfo(Key id, Map mapHoursToCount) { + this.id = id; + this.mapHoursToCount = mapHoursToCount; + } + + public Key getId() { + return this.id; + } + + public void setId(Key id) { + this.id = id; + } + + public Map getMapHoursToCount() { + return this.mapHoursToCount; + } + + public void setMapHoursToCount(Map mapHoursToCount) { + this.mapHoursToCount = mapHoursToCount; + } + + @Override + public String toString() { + return "{" + + " _id='" + getId() + "'" + + ", mapHoursToCount='" + getMapHoursToCount() + "'" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/type/APICatalog.java b/libs/dao/src/main/java/com/akto/dto/type/APICatalog.java new file mode 100644 index 0000000000..e94e650c26 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/type/APICatalog.java @@ -0,0 +1,105 @@ +package com.akto.dto.type; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.bson.codecs.pojo.annotations.BsonId; + +public class APICatalog { + + @BsonId + int id; + Map strictURLToMethods; + Map templateURLToMethods; + List deletedInfo = new ArrayList<>(); + + public APICatalog() { + } + + public APICatalog( + int id, + Map strictURLToMethods, + Map templateURLToMethods + ) { + this.id = id; + this.strictURLToMethods = strictURLToMethods; + this.templateURLToMethods = templateURLToMethods; + } + + public List getAllTypeInfo() { + List ret = new ArrayList<>(); + for(RequestTemplate requestTemplate: strictURLToMethods.values()) { + ret.addAll(requestTemplate.getAllTypeInfo()); + } + + for(Map.Entry urlTemplateAndMethods: templateURLToMethods.entrySet()) { + List singleTypeInfos = urlTemplateAndMethods.getValue().getAllTypeInfo(); + for (SingleTypeInfo singleTypeInfo: singleTypeInfos) { + singleTypeInfo.setUrl(urlTemplateAndMethods.getKey().getTemplateString()); + } + ret.addAll(singleTypeInfos); + } + + return ret; + } + + public APICatalog(int id, Map strictURLToMethods, Map templateURLToMethods, List deletedInfo) { + this.id = id; + this.strictURLToMethods = strictURLToMethods; + this.templateURLToMethods = templateURLToMethods; + this.deletedInfo = deletedInfo; + } + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + public Map getStrictURLToMethods() { + return this.strictURLToMethods; + } + + public void setStrictURLToMethods(Map strictURLToMethods) { + this.strictURLToMethods = strictURLToMethods; + } + + public Map getTemplateURLToMethods() { + return this.templateURLToMethods; + } + + public void setTemplateURLToMethods(Map templateURLToMethods) { + this.templateURLToMethods = templateURLToMethods; + } + + public List getDeletedInfo() { + return this.deletedInfo; + } + + public void setDeletedInfo(List deletedInfo) { + this.deletedInfo = deletedInfo; + } + + public APICatalog id(int id) { + setId(id); + return this; + } + + public static boolean isTemplateUrl(String url) { + return url.contains("STRING") || url.contains("INTEGER"); + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", strictURLToMethods='" + getStrictURLToMethods() + "'" + + ", templateURLToMethods='" + getTemplateURLToMethods() + "'" + + ", deletedInfo='" + getDeletedInfo() + "'" + + "}"; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/type/EndpointInfo.java b/libs/dao/src/main/java/com/akto/dto/type/EndpointInfo.java new file mode 100644 index 0000000000..34cdd64147 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/type/EndpointInfo.java @@ -0,0 +1,29 @@ +package com.akto.dto.type; + +import java.util.Map; + +public class EndpointInfo { + Map> allEndpoints; + + public EndpointInfo() { + } + + public EndpointInfo(Map> allEndpoints) { + this.allEndpoints = allEndpoints; + } + + public Map> getAllEndpoints() { + return this.allEndpoints; + } + + public void setAllEndpoints(Map> allEndpoints) { + this.allEndpoints = allEndpoints; + } + + @Override + public String toString() { + return "{" + + " allEndpoints='" + getAllEndpoints() + "'" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/type/KeyTypes.java b/libs/dao/src/main/java/com/akto/dto/type/KeyTypes.java new file mode 100644 index 0000000000..c556708cd2 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/type/KeyTypes.java @@ -0,0 +1,312 @@ +package com.akto.dto.type; + +import java.util.*; +import java.util.regex.Pattern; + +import com.akto.dao.context.Context; +import com.akto.dto.CustomDataType; +import com.akto.dto.IgnoreData; +import com.akto.dto.SensitiveParamInfo; +import com.akto.dto.type.SingleTypeInfo.ParamId; +import com.akto.dto.type.SingleTypeInfo.SubType; + +import com.akto.types.CappedSet; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.validator.routines.CreditCardValidator; +import org.apache.commons.validator.routines.InetAddressValidator; +import org.json.JSONObject; + +public class KeyTypes { + + public static CreditCardValidator creditCardValidator = new CreditCardValidator(); + public static InetAddressValidator ipAddressValidator = InetAddressValidator.getInstance(); + public static final Map patternToSubType = new HashMap<>(); + static { + patternToSubType.put(SingleTypeInfo.EMAIL, Pattern.compile("^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$", Pattern.CASE_INSENSITIVE)); + patternToSubType.put(SingleTypeInfo.URL, Pattern.compile("^((((https?|ftps?|gopher|telnet|nntp)://)|(mailto:|news:))([-%()_.!~*';/?:@&=+$,A-Za-z0-9])+)$", Pattern.CASE_INSENSITIVE)); + patternToSubType.put(SingleTypeInfo.SSN, Pattern.compile("^\\d{3}-\\d{2}-\\d{4}$", Pattern.CASE_INSENSITIVE)); + patternToSubType.put(SingleTypeInfo.UUID, Pattern.compile("^[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}$", Pattern.CASE_INSENSITIVE)); + } + + Map occurrences; + boolean isSensitive; + + public KeyTypes() { + } + + public KeyTypes(Map occurrences, boolean isSensitive) { + this.occurrences = occurrences; + this.isSensitive = isSensitive; + } + + public List getAllTypeInfo() { + List ret = new ArrayList<>(); + ret.addAll(occurrences.values()); + return ret; + } + + public void process(String url, String method, int responseCode, boolean isHeader, String param, Object object, + String userId, int apiCollectionId, String rawMessage, Map sensitiveParamInfoBooleanMap, + boolean isUrlParam) { + + String key = param.replaceAll("#", ".").replaceAll("\\.\\$", ""); + String[] keyArr = key.split("\\."); + String lastField = keyArr[keyArr.length - 1]; + ParamId paramId = new ParamId(url, method, responseCode, isHeader, param, SingleTypeInfo.GENERIC, apiCollectionId, isUrlParam); + SubType subType = findSubType(object,lastField,paramId); + + SingleTypeInfo singleTypeInfo = occurrences.get(subType); + if (singleTypeInfo == null) { + Set examples = new HashSet<>(); + SingleTypeInfo.Position position = SingleTypeInfo.findPosition(responseCode, isHeader); + if (subType.isSensitive(position)) { + examples.add(rawMessage); + } + + Set userIds = new HashSet<>(); + userIds.add(userId); + paramId.setSubType(subType); + singleTypeInfo = new SingleTypeInfo(paramId, examples, userIds, 0, Context.now(), 0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + + occurrences.put(subType, singleTypeInfo); + } + + singleTypeInfo.setLastSeen(Context.now()); + singleTypeInfo.updateMinMaxValues(object); + + SingleTypeInfo.Domain domain = singleTypeInfo.getDomain(); + if (domain == null || domain == SingleTypeInfo.Domain.ENUM) { + String value = object == null ? "null" : object.toString(); + singleTypeInfo.getValues().add(value); + } + + SensitiveParamInfo sensitiveParamInfo = new SensitiveParamInfo( + singleTypeInfo.getUrl(), singleTypeInfo.getMethod(), singleTypeInfo.getResponseCode(), + singleTypeInfo.getIsHeader(), singleTypeInfo.getParam(), singleTypeInfo.getApiCollectionId(), true + ); + + Boolean result = sensitiveParamInfoBooleanMap.get(sensitiveParamInfo); + if (result != null && !result) { + if (singleTypeInfo.getExamples() == null) { + singleTypeInfo.setExamples(new HashSet<>()); + } + singleTypeInfo.getExamples().add(rawMessage); + sensitiveParamInfoBooleanMap.put(sensitiveParamInfo,true); + } + + singleTypeInfo.incr(); + } + + private static boolean checkForSubtypesTest(ParamId paramId, IgnoreData ignoreData) { + if (ignoreData == null) return true; + if ((paramId != null && paramId.getParam() != null) + && (ignoreData.getIgnoredKeysInAllAPIs().contains(paramId.getParam()) || + (ignoreData.getIgnoredKeysInSelectedAPIs().containsKey(paramId.getParam()) && + ignoreData.getIgnoredKeysInSelectedAPIs().get(paramId.getParam()).contains(paramId)))) { + return false; + } + return true; + } + + public static SubType findSubType(Object o,String key, ParamId paramId) { + + boolean checkForSubtypes = true ; + for (String keyType : SingleTypeInfo.customDataTypeMap.keySet()) { + IgnoreData ignoreData = SingleTypeInfo.customDataTypeMap.get(keyType).getIgnoreData(); + checkForSubtypes = checkForSubtypesTest(paramId, ignoreData); + } + for (String keyType : SingleTypeInfo.aktoDataTypeMap.keySet()) { + IgnoreData ignoreData = SingleTypeInfo.aktoDataTypeMap.get(keyType).getIgnoreData(); + checkForSubtypes = checkForSubtypesTest(paramId, ignoreData); + } + + if (o == null) { + return SingleTypeInfo.NULL; + } + + if (o instanceof Boolean) { + Boolean bool = (Boolean) o; + return bool ? SingleTypeInfo.TRUE : SingleTypeInfo.FALSE; + } + + if(checkForSubtypes){ + for (CustomDataType customDataType: SingleTypeInfo.customDataTypesSortedBySensitivity) { + if (!customDataType.isActive()) continue; + boolean result = customDataType.validate(o,key); + if (result) return customDataType.toSubType(); + } + } + + if (checkForSubtypes && isCreditCard(o.toString())) { + return SingleTypeInfo.CREDIT_CARD; + } + + if (NumberUtils.isDigits(o.toString())) { + if (o.toString().length() < 19) { + o = Long.parseLong(o.toString()); + } + } + + if (o instanceof Long) { + Long l = (Long) o; + + if ( l <= Integer.MAX_VALUE && l >= Integer.MIN_VALUE) { + return SingleTypeInfo.INTEGER_32; + } else { + return SingleTypeInfo.INTEGER_64; + } + } + + if (o instanceof Integer) { + return SingleTypeInfo.INTEGER_32; + } + + if (NumberUtils.isParsable(o.toString())) { + o = Float.parseFloat(o.toString()); + } + + if (o instanceof Float || o instanceof Double) { + return SingleTypeInfo.FLOAT; + } + + if (o instanceof String) { + String str = o.toString(); + for(SubType subType: patternToSubType.keySet()) { + Pattern pattern = patternToSubType.get(subType); + if( ( checkForSubtypes || subType.getName().equals("URL") ) && pattern.matcher(str).matches()) { + return subType; + } + } + if (checkForSubtypes && isJWT(str)) { + return SingleTypeInfo.JWT; + } + + if (checkForSubtypes && isPhoneNumber(str)) { + return SingleTypeInfo.PHONE_NUMBER; + } + + if (checkForSubtypes && isIP(str)) { + return SingleTypeInfo.IP_ADDRESS; + } + + return SingleTypeInfo.GENERIC; + } + + return SingleTypeInfo.OTHER; + } + + public Map getOccurrences() { + return this.occurrences; + } + + public void setOccurrences(Map occurrences) { + this.occurrences = occurrences; + } + + public boolean isIsSensitive() { + return this.isSensitive; + } + + public boolean getIsSensitive() { + return this.isSensitive; + } + + public void setIsSensitive(boolean isSensitive) { + this.isSensitive = isSensitive; + } + + public KeyTypes copy() { + KeyTypes ret = new KeyTypes(new HashMap<>(), false); + for(SubType subType: occurrences.keySet()) { + ret.occurrences.put(subType, occurrences.get(subType).copy()); + } + + return ret; + } + + @Override + public String toString() { + return "{" + + " occurrences='" + getOccurrences() + "'" + + ", isSensitive='" + isIsSensitive() + "'" + + "}"; + } + + public static boolean isPhoneNumber(String mobileNumber) { + boolean lengthCondition = mobileNumber.length() < 8 || mobileNumber.length() > 16; + boolean alphabetsCondition = mobileNumber.toLowerCase() != mobileNumber.toUpperCase(); // contains alphabets + + if (lengthCondition || alphabetsCondition) { + return false; + } + + PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + + // isPossibleNumber computes faster than parse but less accuracy + boolean check = phoneNumberUtil.isPossibleNumber(mobileNumber, + Phonenumber.PhoneNumber.CountryCodeSource.UNSPECIFIED.name()); + if (!check) { + return false; + } + + try { + Phonenumber.PhoneNumber phone = phoneNumberUtil.parse(mobileNumber, + Phonenumber.PhoneNumber.CountryCodeSource.UNSPECIFIED.name()); + return phoneNumberUtil.isValidNumber(phone); + } catch (Exception e) { + // eat it + return false; + } + + } + + public static boolean isJWT(String jwt) { + try { + String[] jwtList = jwt.split("\\."); + if (jwtList.length != 3) // The JWT is composed of three parts + return false; + String jsonFirstPart = new String(Base64.getDecoder().decode(jwtList[0])); + JSONObject firstPart = new JSONObject(jsonFirstPart); // The first part of the JWT is a JSON + if (!firstPart.has("alg")) // The first part has the attribute "alg" + return false; + String jsonSecondPart = new String(Base64.getDecoder().decode(jwtList[1])); + JSONObject secondPart = new JSONObject(jsonSecondPart); // The first part of the JWT is a JSON + }catch (Exception err){ + return false; + } + return true; + } + + public static boolean isCreditCard(String s) { + if (s.length() < 12) return false; + String cc = s.replaceAll(" ", "").replaceAll("-", ""); + if (cc.length() > 23) return false; + if (!cc.toLowerCase().equals(cc.toUpperCase())) return false; // only numbers + return creditCardValidator.isValid(cc); + } + + public static boolean isIP(String s) { + // for edge cases look at test cases of this function + boolean canBeIpv4 = (s.length() > 6) || (s.length() <= 15) && (s.split(".").length == 4); + boolean canBeIpv6 = (s.length() < 45 && s.split(":").length > 6); + if (!(canBeIpv4 || canBeIpv6)) return false; + return ipAddressValidator.isValid(s); + } + + public void merge(KeyTypes that) { + if (that == null || that.getOccurrences() == null) return; + for (SubType subType: that.getOccurrences().keySet()) { + SingleTypeInfo a = this.getOccurrences().get(subType); + SingleTypeInfo b = that.getOccurrences().get(subType); + if (b == null) continue; + if (a == null) { + this.getOccurrences().put(subType, b); + } else { + a.merge(b); + } + } + } + +} diff --git a/libs/dao/src/main/java/com/akto/dto/type/RequestTemplate.java b/libs/dao/src/main/java/com/akto/dto/type/RequestTemplate.java new file mode 100644 index 0000000000..60543045e3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/type/RequestTemplate.java @@ -0,0 +1,783 @@ +package com.akto.dto.type; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; + +import com.akto.dao.context.Context; +import com.akto.dto.HttpResponseParams; +import com.akto.dto.HttpRequestParams; +import com.akto.dto.SensitiveParamInfo; +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.TrafficInfo; +import com.akto.dto.type.SingleTypeInfo.ParamId; +import com.akto.dto.type.SingleTypeInfo.SubType; +import com.akto.dto.type.SingleTypeInfo.SuperType; +import com.akto.dto.type.URLMethods.Method; +import com.akto.types.CappedSet; +import com.akto.util.JSONUtils; +import com.akto.util.Pair; +import com.akto.util.Trie; +import com.akto.util.Trie.Node; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RequestTemplate { + + private static class AllParams { + int lastKnownParamMapSize = 0; + Set paramNames = new HashSet<>(); + + AllParams() {} + + public void rebuild(Set parameterKeys) { + lastKnownParamMapSize = -1; + + for(String p: parameterKeys) { + paramNames.add(getParamName(p)); + } + lastKnownParamMapSize = parameterKeys.size(); + } + + private static String getParamName(String param) { + String paramName = param.substring(param.lastIndexOf('#')+1); + int depth = StringUtils.countMatches(param, '#'); + return depth+"-"+paramName; + } + + } + + private static final Logger logger = LoggerFactory.getLogger(RequestTemplate.class); + + Map parameters; + AllParams allParams = new AllParams(); + Map headers; + Map urlParams = new HashMap<>(); + Map responseTemplates; + Set userIds = new HashSet<>(); + TrafficRecorder trafficRecorder = new TrafficRecorder(); + Trie keyTrie = new Trie(); + + public RequestTemplate() { + } + + public RequestTemplate( + Map parameters, + Map responseTemplates, + Map headers, + TrafficRecorder trafficRecorder + ) { + this.parameters = parameters; + this.headers = headers; + this.responseTemplates = responseTemplates; + this.trafficRecorder = trafficRecorder; + } + + int mergeTimestamp = 0; + + private void add(Set set, String userId) { + if (set.size() < 10) set.add(userId); + } + + public Set getParamNames() { + Set parameterKeys = parameters.keySet(); + if (parameterKeys.size() != allParams.lastKnownParamMapSize) { + allParams.rebuild(parameterKeys); + } + return allParams.paramNames; + } + + private void insert(Object obj, String userId, Trie.Node>> root, String url, String method, int responseCode, String prefix, int apiCollectionId, String rawMessage, Map sensitiveParamInfoBooleanMap) { + + prefix += ("#"+root.getPathElem()); + if (prefix.startsWith("#")) { + prefix = prefix.substring(1); + } + + if (obj instanceof BasicDBObject) { + BasicDBObject basicDBObject = (BasicDBObject) obj; + for(String key: basicDBObject.keySet()) { + Object value = basicDBObject.get(key); + Trie.Node>> curr = root.get(key); + + if (curr == null) { + curr = root.getOrCreate(key, new Pair<>(new KeyTypes(new HashMap<>(), false), new HashSet())); + // logger.info("creating new node for " + key + " in " + url); + } + + add(curr.getValue().getSecond(), userId); + insert(value, userId, curr, url, method, responseCode, prefix, apiCollectionId, rawMessage, sensitiveParamInfoBooleanMap); + } + } else if (obj instanceof BasicDBList) { + for(Object elem: (BasicDBList) obj) { + Trie.Node>> listNode = root.getOrCreate("$", new Pair<>(new KeyTypes(new HashMap<>(), false), new HashSet())); + add(listNode.getValue().getSecond(), userId); + insert(elem, userId, listNode, url, method, responseCode, prefix, apiCollectionId, rawMessage, sensitiveParamInfoBooleanMap); + } + } else { + //url, method, responseCode, true, header, value, userId + root.getValue().getFirst().process(url, method, responseCode, false, prefix, obj, userId, apiCollectionId, rawMessage, sensitiveParamInfoBooleanMap, false); + add(root.getValue().getSecond(), userId); + } + } + + public void processHeaders(Map> headerPayload, String url, String method, int responseCode, String userId, int apiCollectionId, String rawMessage, Map sensitiveParamInfoBooleanMap) { + for (String header: headerPayload.keySet()) { + KeyTypes keyTypes = this.headers.get(header); + if (keyTypes == null) { + keyTypes = new KeyTypes(new HashMap<>(), false); + this.headers.put(header, keyTypes); + } + + for(String value: headerPayload.get(header)) { + keyTypes.process(url, method, responseCode, true, header, value, userId, apiCollectionId, rawMessage, sensitiveParamInfoBooleanMap, false); + } + } + } + + public void processTraffic(int timestamp) { + trafficRecorder.incr(timestamp); + } + + public void recordMessage(String message) { + if (message != null && message.length() > 0) { + trafficRecorder.recordMessage(message); + } + } + + public static long insertTime = 0, processTime = 0, deleteTime = 0; + + public List process2(BasicDBObject payload, String url, String method, int responseCode, String userId, int apiCollectionId, String rawMessage, Map sensitiveParamInfoBooleanMap) { + List deleted = new ArrayList<>(); + + if(userIds.size() < 10) userIds.add(userId); + + Trie.Node>> root = this.keyTrie.getRoot(); + + long s = System.currentTimeMillis(); + // insert(payload, userId, root, url, method, responseCode, "", apiCollectionId); + insertTime += (System.currentTimeMillis() - s); + int now = Context.now(); + + Map> flattened = JSONUtils.flatten(payload); + s = System.currentTimeMillis(); + for(String param: flattened.keySet()) { + if (parameters.size() > 1000) { + continue; + } + KeyTypes keyTypes = parameters.get(param); + if (keyTypes == null) { + + boolean isParentPresent = false; + int curr = param.indexOf("#"); + while (curr < param.length() && curr > 0) { + KeyTypes occ = parameters.get(param.substring(0, curr)); + if (occ != null && occ.occurrences.containsKey(SingleTypeInfo.DICT)) { + isParentPresent = true; + break; + } + curr = param.indexOf("#", curr+1); + } + + + if (isParentPresent) { + continue; + } else { + keyTypes = new KeyTypes(new HashMap<>(), false); + parameters.put(param, keyTypes); + } + } + + for (Object obj: flattened.get(param)) { + keyTypes.process(url, method, responseCode, false, param, obj, userId, apiCollectionId, rawMessage, sensitiveParamInfoBooleanMap, false); + } + } + + processTime += (System.currentTimeMillis() - s); + + s = System.currentTimeMillis(); + if (now - mergeTimestamp > 60 * 2) { +// deleted = tryMergeNodesInTrie(url, method, responseCode, apiCollectionId); + mergeTimestamp = now; + } + + deleteTime += (System.currentTimeMillis() - s); + return deleted; + } + + public List tryMergeNodesInTrie(String url, String method, int responseCode, int apiCollectionId) { + List deletedInfo = new ArrayList<>(); + List mergedNodes = tryMergeNodes(keyTrie.getRoot(), 5, url, method, "", responseCode, apiCollectionId); + + if (mergedNodes == null) { + mergedNodes = new ArrayList<>(); + } + + for(String prefix: mergedNodes) { + Iterator iter = parameters.keySet().iterator(); + + while(iter.hasNext()) { + String paramPath = iter.next(); + if (paramPath.startsWith(prefix)) { + deletedInfo.addAll(parameters.get(paramPath).getAllTypeInfo()); + iter.remove(); + } + } + } + + keyTrie.flatten(parameters); + + return deletedInfo; + } + + public void buildTrie() { + this.keyTrie = new Trie(); + + for(String paramPathStr: this.parameters.keySet()) { + try { + String[] paramPath = paramPathStr.split("#"); + + Node>> curr = this.keyTrie.getRoot(); + for (String path: paramPath) { + curr = curr.getOrCreate(path, new Pair<>(new KeyTypes(new HashMap<>(), false), new HashSet<>())); + } + + curr.getValue().setFirst(this.parameters.get(paramPathStr)); + curr.getValue().setSecond(this.parameters.get(paramPathStr).occurrences.values().iterator().next().userIds); + } catch (Exception e) { + logger.error("exception in " + paramPathStr, e); + } + + } + } + + public Map getParameters() { + return this.parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + public Set getUserIds() { + return this.userIds; + } + + public void setUserIds(Set userIds) { + this.userIds = userIds; + } + + public Map getResponseTemplates() { + return this.responseTemplates; + } + + public void setResponseTemplates(Map responseTemplates) { + this.responseTemplates = responseTemplates; + } + + public Map getHeaders() { + return this.headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public TrafficRecorder getTrafficRecorder() { + return this.trafficRecorder; + } + + public void setTrafficRecorder(TrafficRecorder trafficRecorder) { + this.trafficRecorder = trafficRecorder; + } + + public RequestTemplate copy() { + RequestTemplate ret = new RequestTemplate(new HashMap<>(), new HashMap<>(), new HashMap<>(), new TrafficRecorder(new HashMap<>())); + + for(String parameter: parameters.keySet()) { + ret.parameters.put(parameter, parameters.get(parameter).copy()); + } + + for(String header: headers.keySet()) { + ret.headers.put(header, headers.get(header).copy()); + } + + if (responseTemplates != null) { + for(int code: responseTemplates.keySet()) { + ret.responseTemplates.put(code, responseTemplates.get(code).copy()); + } + } + + ret.userIds = new HashSet<>(); + ret.userIds.addAll(this.userIds); + + ret.keyTrie = this.keyTrie; + ret.trafficRecorder = this.trafficRecorder; + + return ret; + } + + private double varianceDecrease(Set>>> nodes, int thresh) { + + if (nodes == null || nodes.size() < 2) return -1; + + Set userIds = new HashSet<>(); + + for(Node>> node: nodes) { + userIds.addAll(node.getValue().getSecond()); + } + + if (userIds.size() < thresh) return -1; + + double currVariance = 0.0; + + for(Node>> node: nodes) { + currVariance += Math.pow(1 - (node.getValue().getSecond().size()*1.0f)/userIds.size(), 2); + } + + double mergeVariance = 0; + + return currVariance - mergeVariance; + } + + private List tryMergeNodesHelper(Node>> node, int thresh, String url, String method, String prefix, int responseCode, int apiCollectionId) { + + double diff = varianceDecrease(node.getChildren().keySet(), thresh); + + if (diff < 0.5) return null; + + logger.info("flattening trie @" + node.getPathElem() + " in " + url); + + Map occ = new HashMap<>(); + + ParamId paramId = new ParamId(url, method, responseCode, false, prefix + "#" + node.getPathElem(),SingleTypeInfo.DICT, apiCollectionId, false); + occ.put(SingleTypeInfo.DICT, new SingleTypeInfo(paramId, new HashSet<>(), node.getValue().getSecond(), 1, Context.now(), 0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE)); + node.getValue().setFirst(new KeyTypes(occ, false)); + + node.getChildren().clear(); + List ret = new ArrayList<>(); + + ret.add(prefix); + return ret; + } + + public List tryMergeNodes(Node>> node, int thresh, String url, String method, String prefix, int responseCode, int apiCollectionId) { + prefix += ("#"+node.getPathElem()); + if (prefix.startsWith("#")) { + prefix = prefix.substring(1); + } + + List mergedNodes = tryMergeNodesHelper(node, thresh, url, method, prefix, responseCode, apiCollectionId); + boolean merged = mergedNodes != null && mergedNodes.size() > 0; + + if (!merged && node.getChildren().size() > 0) { + for(Node>> child: node.getChildren().keySet()) { + List childMerges = tryMergeNodes(child, thresh, url, method, prefix, responseCode, apiCollectionId); + if (childMerges != null) { + if (mergedNodes == null) { + mergedNodes = childMerges; + } else { + mergedNodes.addAll(childMerges); + } + } + } + } + + return mergedNodes; + } + + @Override + public String toString() { + return "{" + + " parameters='" + getParameters() + "'" + + ", responseTemplates='" + getResponseTemplates() + "'" + + ", headers='" + getHeaders() + "'" + + ", userIds='" + userIds + "'" + + "}"; + } + + public void mergeFrom(RequestTemplate that) { + for(String paramName: that.parameters.keySet()) { + KeyTypes thisKeyTypes = this.parameters.get(paramName); + KeyTypes thatKeyTypes = that.parameters.get(paramName); + if (thisKeyTypes == null) { + this.parameters.put(paramName, thatKeyTypes.copy()); + } else { + thisKeyTypes.merge(thatKeyTypes); + } + } + + for(String header: that.headers.keySet()) { + KeyTypes thisKeyTypes = this.headers.get(header); + KeyTypes thatKeyTypes = that.headers.get(header); + if (thisKeyTypes == null) { + this.headers.put(header, thatKeyTypes.copy()); + } else { + thisKeyTypes.merge(thatKeyTypes); + } + } + + + if (that.responseTemplates != null) { + + for(int statusCode: that.responseTemplates.keySet()) { + RequestTemplate thisResp = this.responseTemplates.get(statusCode); + RequestTemplate thatResp = that.responseTemplates.get(statusCode); + if (thisResp == null) { + this.responseTemplates.put(statusCode, thatResp.copy()); + } else { + thisResp.mergeFrom(thatResp); + } + } + } + + this.userIds.addAll(that.userIds); + + this.keyTrie.getRoot().mergeFrom(that.keyTrie.getRoot(), MergeTrieKeyFunc.instance); + + this.trafficRecorder.mergeFrom(that.getTrafficRecorder()); + } + + private static class MergeTrieKeyFunc implements BiConsumer>,Pair>> { + + public static final MergeTrieKeyFunc instance = new MergeTrieKeyFunc(); + + @Override + public void accept(Pair> t, Pair> u) { + t.getFirst().getOccurrences().putAll(u.getFirst().getOccurrences()); + t.getSecond().addAll(u.getSecond()); + } + + } + + public List getAllTypeInfo() { + List ret = new ArrayList<>(); + + for(KeyTypes k: parameters.values()) { + ret.addAll(k.getAllTypeInfo()); + } + + for(KeyTypes k: headers.values()) { + ret.addAll(k.getAllTypeInfo()); + } + + for (KeyTypes k: urlParams.values()) { + ret.addAll(k.getAllTypeInfo()); + } + + if (responseTemplates != null) { + for(RequestTemplate responseParams: responseTemplates.values()) { + ret.addAll(responseParams.getAllTypeInfo()); + } + } + return ret; + } + + public List removeAllTrafficInfo(int apiCollectionId, String url, Method method, int responseCode) { + List ret = new ArrayList<>(); + if (!trafficRecorder.isEmpty()) { + Set hoursSince1970 = trafficRecorder.getTrafficMapSinceLastSync().keySet(); + int start = Integer.parseInt(hoursSince1970.iterator().next())/24/30; + int end = start + 1; + TrafficInfo trafficInfo = new TrafficInfo(new Key(apiCollectionId, url, method, responseCode, start, end), trafficRecorder.getTrafficMapSinceLastSync()); + ret.add(trafficInfo); + } + + trafficRecorder.setTrafficMapSinceLastSync(new HashMap<>()); + + if (responseTemplates != null && responseTemplates.size() > 0) { + for(Map.Entry entry: responseTemplates.entrySet()) { + ret.addAll(entry.getValue().removeAllTrafficInfo(apiCollectionId, url, method, entry.getKey())); + } + } + + return ret; + } + + public List removeAllSampleMessage() { + List ret = new ArrayList<>(); + ret.addAll(trafficRecorder.getSampleMessages().get()); + trafficRecorder.getSampleMessages().get().clear(); + return ret; + } + + public static boolean isMergedOnStr(URLTemplate urlTemplate) { + for(SuperType superType: urlTemplate.getTypes()) { + if (superType == SuperType.STRING) { + return true; + } + } + + return false; + } + + private static boolean isWithin20Percent(Set xSet, Set ySet) { + + if (xSet == null) xSet = new HashSet<>(); + if (ySet == null) ySet = new HashSet<>(); + + int xs = xSet.size(); + int ys = ySet.size(); + if (xs == 0) return ys == 0; + if (ys == 0) return xs == 0; + + int allowedDiffs = (int) (0.2 * Math.min(xs, ys)); + + if (Math.abs(xs - ys) > allowedDiffs) return false; + + int absentInY = 0; + for(String x: xSet) { + if (!ySet.contains(x)) { + absentInY ++; + if (absentInY > allowedDiffs) return false; + } + } + + allowedDiffs = allowedDiffs - absentInY; + + int absentInX = 0; + for(String y: ySet) { + if (!xSet.contains(y)) { + absentInX ++; + if (absentInX > allowedDiffs) return false; + } + } + + return absentInX <= allowedDiffs; + } + + + private static Map> groupByResponseCode(Set xSet) { + Map> ret = new HashMap<>(); + for(String x: xSet) { + int responseCode = Integer.parseInt(x.split(" ")[0]); + String param = x.split(" ")[1]; + + Set params = ret.get(responseCode); + if (params == null) { + params = new HashSet<>(); + ret.put(responseCode, params); + } + + params.add(param); + } + return ret; + } + + public static boolean compareKeys(Set aReq, Set bReq, URLTemplate mergedUrl) { + Map> aParams = groupByResponseCode(aReq); + Map> bParams = groupByResponseCode(bReq); + + if (!isMergedOnStr(mergedUrl)) { + return true; + } + + if (!isWithin20Percent(aParams.get(-1), bParams.get(-1))) { + return false; + } + + aParams.remove(-1); + bParams.remove(-1); + + for (int aStatus: aParams.keySet()) { + if (HttpResponseParams.validHttpResponseCode(aStatus)) { + if (!bParams.containsKey(aStatus)) { + return false; + } + } + } + + for (int bStatus: bParams.keySet()) { + if (HttpResponseParams.validHttpResponseCode(bStatus)) { + if (!aParams.containsKey(bStatus)) { + return false; + } + } + } + + for (int aStatus: aParams.keySet()) { + if (HttpResponseParams.validHttpResponseCode(aStatus)) { + Set aResp = aParams.get(aStatus); + Set bResp = bParams.get(aStatus); + + if (aResp == null || bResp == null) { + return false; + } + + if (!isWithin20Percent(aResp, bResp)) { + return false; + } + } + } + + return true; + } + + private static boolean compareKeys(RequestTemplate a, RequestTemplate b, URLTemplate mergedUrl) { + if (!isMergedOnStr(mergedUrl)) { + return true; + } + + if (!isWithin20Percent(a.getParamNames(), b.getParamNames())) { + return false; + } + + for (int aStatus: a.getResponseTemplates().keySet()) { + if (HttpResponseParams.validHttpResponseCode(aStatus)) { + if (!b.getResponseTemplates().containsKey(aStatus)) { + return false; + } + } + } + + for (int bStatus: b.getResponseTemplates().keySet()) { + if (HttpResponseParams.validHttpResponseCode(bStatus)) { + if (!a.getResponseTemplates().containsKey(bStatus)) { + return false; + } + } + } + + boolean has2XXStatus = false; + for (int aStatus: a.getResponseTemplates().keySet()) { + if (HttpResponseParams.validHttpResponseCode(aStatus)) { + has2XXStatus = true; + RequestTemplate aResp = a.getResponseTemplates().get(aStatus); + RequestTemplate bResp = b.getResponseTemplates().get(aStatus); + + if (aResp == null || bResp == null) { + return false; + } + + if (!isWithin20Percent(aResp.getParamNames(), bResp.getParamNames())) { + return false; + } + } + } + + return has2XXStatus; + } + + public boolean compare(RequestTemplate that, URLTemplate mergedUrl) { + return compareKeys(this, that, mergedUrl); + } + + public static BasicDBObject parseRequestPayload(HttpRequestParams requestParams, String urlWithParams) { + String reqPayload = requestParams.getPayload(); + + return parseRequestPayload(reqPayload, urlWithParams); + } + + public static BasicDBObject parseRequestPayload(String reqPayload, String urlWithParams) { + + BasicDBObject queryParams = getQueryJSON(urlWithParams); + + if (reqPayload == null || reqPayload.isEmpty()) { + reqPayload = "{}"; + } + + if(reqPayload.startsWith("[")) { + reqPayload = "{\"json\": "+reqPayload+"}"; + } + + + BasicDBObject payload = null; + try { + payload = BasicDBObject.parse(reqPayload); + } catch (Exception e) { + payload = BasicDBObject.parse("{}"); + } + + payload.putAll(queryParams.toMap()); + + return payload; + } + + public static BasicDBObject getQueryJSON(String url) { + BasicDBObject ret = new BasicDBObject(); + if (url == null) { + return ret; + } + + String[] splitURL = url.split("\\?"); + + if (splitURL.length != 2) { + return ret; + } + + String queryParamsStr = splitURL[1]; + if (queryParamsStr == null) { + return ret; + } + + String[] queryParams = queryParamsStr.split("&"); + + for (String queryParam : queryParams) { + String[] keyVal = queryParam.split("="); + if (keyVal.length != 2) { + continue; + } + try { + keyVal[0] = URLDecoder.decode(keyVal[0], "UTF-8"); + keyVal[1] = URLDecoder.decode(keyVal[1], "UTF-8"); + ret.put(keyVal[0], keyVal[1]); + } catch (UnsupportedEncodingException e) { + continue; + } + } + + return ret; + } + + public Map getUrlParams() { + return urlParams; + } + + public void setUrlParams(Map urlParams) { + this.urlParams = urlParams; + } + + // unit tests for "fillUrlParams" written in TestApiCatalogSync + public void fillUrlParams(String[] tokenizedUrl, URLTemplate urlTemplate, int apiCollectionId) { + if (this.urlParams == null) this.urlParams = new HashMap<>(); + + SuperType[] types = urlTemplate.getTypes(); + String url = urlTemplate.getTemplateString(); + String method = urlTemplate.getMethod().name(); + + if (tokenizedUrl.length != types.length) return; + + for (int idx=0; idx < types.length; idx++) { + SuperType superType = types[idx]; + if (superType == null) continue; + Object val = tokenizedUrl[idx]; + + if (superType.equals(SuperType.INTEGER)) { + val = Integer.parseInt(val.toString()); + } else if (superType.equals(SuperType.FLOAT)) { + val = Float.parseFloat(val.toString()); + } + + KeyTypes keyTypes = this.urlParams.get(idx); + if (keyTypes == null) { + keyTypes = new KeyTypes(new HashMap<>(), false); + this.urlParams.put(idx, keyTypes); + } + + String userId = ""; + keyTypes.process(url, method, -1, false, idx+"", val,userId, apiCollectionId, "", new HashMap<>(), true); + + } + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/type/SingleTypeInfo.java b/libs/dao/src/main/java/com/akto/dto/type/SingleTypeInfo.java new file mode 100644 index 0000000000..4672fbd9a1 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/type/SingleTypeInfo.java @@ -0,0 +1,800 @@ +package com.akto.dto.type; + +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.akto.dao.AktoDataTypeDao; +import com.akto.dao.CustomAuthTypeDao; +import com.akto.dao.CustomDataTypeDao; +import com.akto.dao.context.Context; +import com.akto.dto.AktoDataType; +import com.akto.dto.CustomAuthType; +import com.akto.dto.CustomDataType; +import com.akto.types.CappedSet; +import com.mongodb.BasicDBObject; +import io.swagger.v3.oas.models.media.*; +import org.apache.commons.lang3.StringUtils; +import org.bson.codecs.pojo.annotations.BsonIgnore; +import org.bson.codecs.pojo.annotations.BsonProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.primitives.Longs.min; +import static java.lang.Long.max; + +public class SingleTypeInfo { + + private static final Logger logger = LoggerFactory.getLogger(SingleTypeInfo.class); + public static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + public static void init() { + scheduler.scheduleAtFixedRate(new Runnable() { + public void run() { + fetchCustomDataTypes(); + fetchCustomAuthTypes(); + } + }, 0, 5, TimeUnit.MINUTES); + + } + + public static String findLastKeyFromParam(String param) { + if (param == null) return null; + String paramReplaced = param.replaceAll("#", ".").replaceAll("\\.\\$", ""); + String[] paramList = paramReplaced.split("\\."); + return paramList[paramList.length-1]; // choosing the last key + } + + public static void fetchCustomDataTypes() { + Context.accountId.set(1_000_000); + try { + List customDataTypes = CustomDataTypeDao.instance.findAll(new BasicDBObject()); + Map newMap = new HashMap<>(); + List sensitiveCustomDataType = new ArrayList<>(); + List nonSensitiveCustomDataType = new ArrayList<>(); + for (CustomDataType customDataType: customDataTypes) { + newMap.put(customDataType.getName(), customDataType); + if (customDataType.isSensitiveAlways() || customDataType.getSensitivePosition().size()>0) { + sensitiveCustomDataType.add(customDataType); + } else { + nonSensitiveCustomDataType.add(customDataType); + } + } + customDataTypeMap = newMap; + sensitiveCustomDataType.addAll(nonSensitiveCustomDataType); + customDataTypesSortedBySensitivity = new ArrayList<>(sensitiveCustomDataType); + + List aktoDataTypes = AktoDataTypeDao.instance.findAll(new BasicDBObject()); + Map newAktoMap = new HashMap<>(); + for(AktoDataType aktoDataType:aktoDataTypes){ + if(subTypeMap.containsKey(aktoDataType.getName())){ + newAktoMap.put(aktoDataType.getName(), aktoDataType); + subTypeMap.get(aktoDataType.getName()).setSensitiveAlways(aktoDataType.getSensitiveAlways()); + subTypeMap.get(aktoDataType.getName()).setSensitivePosition(aktoDataType.getSensitivePosition()); + } + } + aktoDataTypeMap = newAktoMap; + } catch (Exception ex) { + } + } + + public static List activeCustomAuthTypes = new ArrayList<>(); + + public static void fetchCustomAuthTypes() { + activeCustomAuthTypes = CustomAuthTypeDao.instance.findAll(CustomAuthType.ACTIVE,true); + } + + public enum SuperType { + BOOLEAN, INTEGER, FLOAT, STRING, NULL, OTHER, CUSTOM + } + + public enum Position { + REQUEST_HEADER, REQUEST_PAYLOAD, RESPONSE_HEADER, RESPONSE_PAYLOAD + } + + public static final SubType TRUE = new SubType("TRUE", false, SuperType.BOOLEAN, BooleanSchema.class, + Collections.emptyList()); + public static final SubType FALSE = new SubType("FALSE", false, SuperType.BOOLEAN, BooleanSchema.class, + Collections.emptyList()); + public static final SubType INTEGER_32 = new SubType("INTEGER_32", false, SuperType.INTEGER, IntegerSchema.class, + Collections.emptyList()); + public static final SubType INTEGER_64 = new SubType("INTEGER_64", false, SuperType.INTEGER, IntegerSchema.class, + Collections.emptyList()); + public static final SubType FLOAT = new SubType("FLOAT", false, SuperType.FLOAT, NumberSchema.class, + Collections.emptyList()); + public static final SubType NULL = new SubType("NULL", false, SuperType.STRING, StringSchema.class, + Collections.emptyList()); + public static final SubType OTHER = new SubType("OTHER", false, SuperType.STRING, StringSchema.class, + Collections.emptyList()); + public static SubType EMAIL = new SubType("EMAIL", true, SuperType.STRING, EmailSchema.class, + Collections.emptyList()); + public static final SubType URL = new SubType("URL", false, SuperType.STRING, StringSchema.class, + Collections.emptyList()); + public static SubType ADDRESS = new SubType("ADDRESS", true, SuperType.STRING, StringSchema.class, + Collections.emptyList()); + public static SubType SSN = new SubType("SSN", true, SuperType.STRING, StringSchema.class, + Collections.emptyList()); + public static SubType CREDIT_CARD = new SubType("CREDIT_CARD", true, SuperType.STRING, StringSchema.class, + Collections.emptyList()); + public static SubType PHONE_NUMBER = new SubType("PHONE_NUMBER", true, SuperType.STRING, StringSchema.class, + Collections.emptyList()); + public static SubType UUID = new SubType("UUID", false, SuperType.STRING, StringSchema.class, + Collections.emptyList()); + public static final SubType GENERIC = new SubType("GENERIC", false, SuperType.STRING, StringSchema.class, + Collections.emptyList()); + public static final SubType DICT = new SubType("DICT", false, SuperType.STRING, MapSchema.class, + Collections.emptyList()); + public static SubType JWT = new SubType("JWT", false, SuperType.STRING, StringSchema.class, + Arrays.asList(Position.RESPONSE_PAYLOAD, Position.RESPONSE_HEADER)); + public static SubType IP_ADDRESS = new SubType("IP_ADDRESS", false, SuperType.STRING, StringSchema.class, + Arrays.asList(Position.RESPONSE_PAYLOAD, Position.RESPONSE_HEADER)); + // make sure to add AKTO subTypes to subTypeMap below + + public static class SubType { + private String name; + private boolean sensitiveAlways; + private SuperType superType; + private Class swaggerSchemaClass; + private List sensitivePosition; + + public SubType() { + } + + public SubType(String name, boolean sensitiveAlways, SuperType superType, + Class swaggerSchemaClass, List sensitivePosition) { + this.name = name; + this.sensitiveAlways = sensitiveAlways; + this.superType = superType; + this.swaggerSchemaClass = swaggerSchemaClass; + this.sensitivePosition = sensitivePosition; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SubType subType = (SubType) o; + return sensitiveAlways == subType.sensitiveAlways && name.equals(subType.name) && superType == subType.superType && swaggerSchemaClass.equals(subType.swaggerSchemaClass) && sensitivePosition.equals(subType.sensitivePosition); + } + + @Override + public int hashCode() { + return Objects.hash(name, sensitiveAlways, superType, swaggerSchemaClass, sensitivePosition); + } + + @Override + public String toString() { + return "SubType{" + + "name='" + name + '\'' + + ", sensitiveAlways=" + sensitiveAlways + + ", superType=" + superType + + ", swaggerSchemaClass=" + swaggerSchemaClass + + ", sensitivePosition=" + sensitivePosition + + '}'; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isSensitiveAlways() { + return sensitiveAlways; + } + + public void setSensitiveAlways(boolean sensitiveAlways) { + this.sensitiveAlways = sensitiveAlways; + } + + public SuperType getSuperType() { + return superType; + } + + public void setSuperType(SuperType superType) { + this.superType = superType; + } + + public Class getSwaggerSchemaClass() { + return swaggerSchemaClass; + } + + public void setSwaggerSchemaClass(Class swaggerSchemaClass) { + this.swaggerSchemaClass = swaggerSchemaClass; + } + + public List getSensitivePosition() { + return sensitivePosition; + } + + public void setSensitivePosition(List sensitivePosition) { + this.sensitivePosition = sensitivePosition; + } + + // Calculates and tells if sensitive or not based on sensitiveAlways and sensitivePosition fields + public boolean isSensitive(Position position) { + if (this.sensitiveAlways) return true; + return this.sensitivePosition.contains(position); + } + } + + public Position findPosition() { + return findPosition(responseCode, isHeader); + } + + public static Position findPosition(int responseCode, boolean isHeader) { + SingleTypeInfo.Position position; + if (responseCode == -1) { + if (isHeader) { + position = SingleTypeInfo.Position.REQUEST_HEADER; + } else { + position = SingleTypeInfo.Position.REQUEST_PAYLOAD; + } + } else { + if (isHeader) { + position = SingleTypeInfo.Position.RESPONSE_HEADER; + } else { + position = SingleTypeInfo.Position.RESPONSE_PAYLOAD; + } + } + + return position; + } + + public static class ParamId { + String url; + String method; + int responseCode; + boolean isHeader; + String param; + @BsonIgnore + SubType subType; + int apiCollectionId; + @BsonProperty("subType") + String subTypeString; + boolean isUrlParam; + public ParamId(String url, String method, int responseCode, boolean isHeader, String param, SubType subType, + int apiCollectionId, boolean isUrlParam) { + this.url = url; + this.method = method; + this.responseCode = responseCode; + this.isHeader = isHeader; + this.param = param; + this.subType = subType; + this.apiCollectionId = apiCollectionId; + this.isUrlParam = isUrlParam; + } + + public ParamId() { + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public int getResponseCode() { + return responseCode; + } + + public void setResponseCode(int responseCode) { + this.responseCode = responseCode; + } + + public boolean isHeader() { + return isHeader; + } + + public boolean getIsHeader() { + return isHeader; + } + + public void setIsHeader(boolean header) { + isHeader = header; + } + + public String getParam() { + return param; + } + + public void setParam(String param) { + this.param = param; + } + + public SubType getSubType() { + return subType; + } + + public void setSubType(SubType subType) { + this.subType = subType; + } + + public String getSubTypeString() { + if (subType == null) return null; + return subType.name; + } + + public void setSubTypeString(String subTypeString) { + this.subTypeString = subTypeString; + this.subType = subTypeMap.get(subTypeString); + if (this.subType == null) { + CustomDataType customDataType = customDataTypeMap.get(subTypeString); + if (customDataType != null) { + this.subType = customDataType.toSubType(); + } else { + this.subType = GENERIC; + } + } + } + + public int getApiCollectionId() { + return apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public boolean getIsUrlParam() { + return isUrlParam; + } + + public void setIsUrlParam(boolean urlParam) { + isUrlParam = urlParam; + } + } + + public static final String _URL = "url"; + String url; + public static final String _METHOD = "method"; + String method; + public static final String _RESPONSE_CODE = "responseCode"; + int responseCode; + public static final String _IS_HEADER = "isHeader"; + boolean isHeader; + public static final String _PARAM = "param"; + String param; + public static final String SUB_TYPE = "subType"; + @BsonIgnore + SubType subType; + public static final String SUBTYPE_STRING = "subTypeString"; + @BsonProperty("subType") + String subTypeString; + public static final String _EXAMPLES = "examples"; + @BsonIgnore + Set examples = new HashSet<>(); + public static final String _USER_IDS = "userIds"; + @BsonIgnore + Set userIds = new HashSet<>(); + public static final String _COUNT = "count"; + int count; + public static final String _TIMESTAMP = "timestamp"; + int timestamp; + public static final String _DURATION = "duration"; + int duration; + public static final String _API_COLLECTION_ID = "apiCollectionId"; + int apiCollectionId; + public static final String _SENSITIVE = "sensitive"; + @BsonIgnore + boolean sensitive; + public static final String _IS_URL_PARAM = "isUrlParam"; + boolean isUrlParam; + public static final String _VALUES = "values"; + public static final int VALUES_LIMIT = 50; + CappedSet values = new CappedSet<>(); + public static final String _DOMAIN = "domain"; + Domain domain = Domain.ENUM; + public static final String MIN_VALUE = "minValue"; + public static final long ACCEPTED_MAX_VALUE = Long.MAX_VALUE - 100_000; + long minValue = ACCEPTED_MAX_VALUE; // this value will be used when field doesn't exist in db + public static final String MAX_VALUE = "maxValue"; + public static final long ACCEPTED_MIN_VALUE = Long.MIN_VALUE + 100_000; + long maxValue = ACCEPTED_MIN_VALUE; // this value will be used when field doesn't exist in db + public static final String LAST_SEEN = "lastSeen"; + long lastSeen; + + @BsonIgnore + private boolean isPrivate; // do not use this field anywhere else. This was added to convey if STI is private or not to frontend + @BsonIgnore + private Object value; + + public static final String _UNIQUE_COUNT = "uniqueCount"; + public long uniqueCount = 0L; + public static final String _PUBLIC_COUNT = "publicCount"; + public long publicCount = 0L; + public static final double THRESHOLD = 0.1; + + public enum Domain { + ENUM, RANGE, ANY + } + + public static final Map subTypeMap = new HashMap<>(); + public static Map customDataTypeMap = new HashMap<>(); + public static List customDataTypesSortedBySensitivity = new ArrayList<>(); + public static Map aktoDataTypeMap = new HashMap<>(); + static { + subTypeMap.put("TRUE", TRUE); + subTypeMap.put("FALSE", FALSE); + subTypeMap.put("INTEGER_32", INTEGER_32); + subTypeMap.put("INTEGER_64", INTEGER_64); + subTypeMap.put("FLOAT", FLOAT); + subTypeMap.put("NULL", NULL); + subTypeMap.put("OTHER", OTHER); + subTypeMap.put("EMAIL", EMAIL); + subTypeMap.put("URL", URL); + subTypeMap.put("ADDRESS", ADDRESS); + subTypeMap.put("SSN", SSN); + subTypeMap.put("CREDIT_CARD", CREDIT_CARD); + subTypeMap.put("PHONE_NUMBER", PHONE_NUMBER); + subTypeMap.put("UUID", UUID); + subTypeMap.put("GENERIC", GENERIC); + subTypeMap.put("DICT", DICT); + subTypeMap.put("JWT", JWT); + subTypeMap.put("IP_ADDRESS", IP_ADDRESS); + } + + public SingleTypeInfo() { + } + + public SingleTypeInfo(ParamId paramId, Set examples, Set userIds, int count, int timestamp, + int duration, CappedSet values, Domain domain, long minValue, long maxValue) { + this.url = paramId.url; + this.method = paramId.method; + this.responseCode = paramId.responseCode; + this.isHeader = paramId.isHeader; + this.param = paramId.param; + this.subType = paramId.subType; + this.apiCollectionId = paramId.apiCollectionId; + this.isUrlParam = paramId.isUrlParam; + this.examples = examples; + this.userIds = userIds; + this.count = count; + this.timestamp = timestamp; + this.duration = duration; + this.lastSeen = Context.now(); + this.values = values; + this.domain = domain; + this.minValue = minValue; + this.maxValue = maxValue; + } + + public String composeKey() { + return composeKey(url, method, responseCode, isHeader, param, subType, apiCollectionId, isUrlParam); + } + +public String composeKeyWithCustomSubType(SubType s) { + return composeKey(url, method, responseCode, isHeader, param, s, apiCollectionId, isUrlParam); + } + + public static String composeKey(String url, String method, int responseCode, boolean isHeader, String param, SubType subType, int apiCollectionId, boolean isUrlParam) { + return StringUtils.joinWith("@", url, method, responseCode, isHeader, param, subType, apiCollectionId, isUrlParam); + } + + + public void incr() { + this.count++; + } + + public SingleTypeInfo copy() { + Set copyExamples = new HashSet<>(); + copyExamples.addAll(this.examples); + + Set copyUserIds = new HashSet<>(); + copyUserIds.addAll(this.userIds); + + CappedSet copyValues = new CappedSet<>(new HashSet<>(this.values.getElements())); + + ParamId paramId = new ParamId(); + paramId.url = url; + paramId.method = method; + paramId.responseCode = responseCode; + paramId.isHeader = isHeader; + paramId.param = param; + paramId.subType = new SubType(subType.name, subType.sensitiveAlways, subType.superType, subType.swaggerSchemaClass, subType.sensitivePosition); + paramId.apiCollectionId = apiCollectionId; + paramId.isUrlParam = isUrlParam; + + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, copyExamples, copyUserIds, this.count, + this.timestamp, this.duration, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + singleTypeInfo.setValues(copyValues); + singleTypeInfo.minValue = this.minValue; + singleTypeInfo.maxValue = this.maxValue; + singleTypeInfo.domain = this.domain; + singleTypeInfo.uniqueCount = this.uniqueCount; + singleTypeInfo.publicCount = this.publicCount; + return singleTypeInfo; + } + + public String getUrl() { + return this.url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getMethod() { + return this.method; + } + + public void setMethod(String method) { + this.method = method; + } + + public int getResponseCode() { + return this.responseCode; + } + + public void setResponseCode(int responseCode) { + this.responseCode = responseCode; + } + + public boolean isIsHeader() { + return this.isHeader; + } + + public boolean getIsHeader() { + return this.isHeader; + } + + public void setIsHeader(boolean isHeader) { + this.isHeader = isHeader; + } + + public String getParam() { + return this.param; + } + + public void setParam(String param) { + this.param = param; + } + + public SubType getSubType() { + return this.subType; + } + + public void setSubType(SubType subType) { + this.subType = subType; + } + + public Set getExamples() { + return this.examples; + } + + public void setExamples(Set examples) { + this.examples = examples; + } + + public Set getUserIds() { + return this.userIds; + } + + public void setUserIds(Set userIds) { + this.userIds = userIds; + } + + public int getCount() { + return this.count; + } + + public void setCount(int count) { + this.count = count; + } + + public int getTimestamp() { + return this.timestamp; + } + + public void setTimestamp(int timestamp) { + this.timestamp = timestamp; + } + + public int getDuration() { + return this.duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } + + public int getApiCollectionId() { + return this.apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } + + public String getSubTypeString() { + if (subType == null) return null; + return subType.name; + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof SingleTypeInfo)) { + return false; + } + SingleTypeInfo singleTypeInfo = (SingleTypeInfo) o; + return url.equals(singleTypeInfo.url) && + method.equals(singleTypeInfo.method) && + responseCode == singleTypeInfo.responseCode && + isHeader == singleTypeInfo.isHeader && + param.equals(singleTypeInfo.param) && + subType.equals(singleTypeInfo.subType) && + apiCollectionId == singleTypeInfo.apiCollectionId && + isUrlParam == singleTypeInfo.isUrlParam; + } + + @Override + public int hashCode() { + return Objects.hash(url, method, responseCode, isHeader, param, subType, apiCollectionId, isUrlParam); + } + + @Override + public String toString() { + return "{" + + " url='" + getUrl() + "'" + + ", method='" + getMethod() + "'" + + ", responseCode='" + getResponseCode() + "'" + + ", isHeader='" + isIsHeader() + "'" + + ", param='" + getParam() + "'" + + ", subType='" + getSubType().name + "'" + + ", apiCollectionId='" + getApiCollectionId() + "'" + + ", examples='" + getExamples() + "'" + + ", userIds='" + getUserIds() + "'" + + ", count='" + getCount() + "'" + + ", timestamp='" + getTimestamp() + "'" + + ", duration='" + getDuration() + "'" + + "}"; + } + + public void setSubTypeString(String subTypeString) { + this.subTypeString = subTypeString; + this.subType = subTypeMap.get(subTypeString); + if (this.subType == null) { + CustomDataType customDataType = customDataTypeMap.get(subTypeString); + if (customDataType != null) { + this.subType = customDataType.toSubType(); + } else { + this.subType = GENERIC; + } + } + } + + public boolean getSensitive() { + if (this.subType == null) return false; // this was done for paramStateAction because it uses projections and doesn't return subType + return this.subType.isSensitive(this.findPosition()); + } + + + public CappedSet getValues() { + return values; + } + + public void setValues(CappedSet values) { + this.values = values; + } + + public Domain getDomain() { + return domain; + } + + public void setDomain(Domain domain) { + this.domain = domain; + } + + public boolean getIsUrlParam() { + return isUrlParam; + } + + public void setIsUrlParam(boolean urlParam) { + isUrlParam = urlParam; + } + + public void updateMinMaxValues(Object o) { + if (subType.getSuperType() == SingleTypeInfo.SuperType.INTEGER || subType.getSuperType() == SingleTypeInfo.SuperType.FLOAT) { + try { + // this is done so that both integer and decimal values can be parsed + // But while storing double we omit the decimal part + double d = Double.parseDouble(o.toString()); + long l = (long) d; + this.minValue = min(this.minValue, l); + this.minValue = max(this.minValue, ACCEPTED_MIN_VALUE); + + this.maxValue = max(this.maxValue, l); + this.maxValue = min(this.maxValue, ACCEPTED_MAX_VALUE); + if (this.maxValue > ACCEPTED_MAX_VALUE) { + this.maxValue = ACCEPTED_MAX_VALUE; + } + } catch (Exception e) { + logger.error("ERROR: while parsing long for min max in sti " + o.toString()); + } + } + } + + public void incPublicCount(int c) { + this.publicCount += c; + } + + public void incUniqueCount(int c) { + this.uniqueCount += c; + } + + public void merge(SingleTypeInfo that) { + if (that != null) { + this.count += that.getCount(); + this.values.getElements().addAll(that.values.getElements()); + this.minValue = min(this.minValue, that.minValue); + this.maxValue = max(this.maxValue, that.maxValue); + this.lastSeen = max(this.lastSeen, that.lastSeen); + this.publicCount += that.publicCount; + this.uniqueCount += that.uniqueCount; + } + } + + public void clearValues() { + this.values = new CappedSet<>(); + } + + public boolean getIsPrivate() { + if (uniqueCount == 0) return true; + double v = (1.0*publicCount) / uniqueCount; + return v <= SingleTypeInfo.THRESHOLD; + } + + public long getMinValue() { + return minValue; + } + + public void setMinValue(long minValue) { + this.minValue = minValue; + } + + public long getMaxValue() { + return maxValue; + } + + public void setMaxValue(long maxValue) { + this.maxValue = maxValue; + } + + public long getLastSeen() { + return lastSeen; + } + + public void setLastSeen(long lastSeen) { + this.lastSeen = lastSeen; + } + + public long getUniqueCount() { + return uniqueCount; + } + + public void setUniqueCount(long uniqueCount) { + this.uniqueCount = uniqueCount; + } + + public long getPublicCount() { + return publicCount; + } + + public void setPublicCount(long publicCount) { + this.publicCount = publicCount; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/type/TrafficRecorder.java b/libs/dao/src/main/java/com/akto/dto/type/TrafficRecorder.java new file mode 100644 index 0000000000..7c34c00318 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/type/TrafficRecorder.java @@ -0,0 +1,61 @@ +package com.akto.dto.type; + +import java.util.HashMap; +import java.util.Map; + +import com.akto.types.CappedList; + +public class TrafficRecorder { + + Map trafficMapSinceLastSync = new HashMap<>(); + CappedList sampleMessages = new CappedList(10, true); + public void incr(int timestamp) { + int hoursSinceEpoch = timestamp/3600; + trafficMapSinceLastSync.compute(""+hoursSinceEpoch, (k,v) -> { return v == null ? 1 : ++v;}); + } + + public TrafficRecorder() { + } + + public void recordMessage(String message) { + this.sampleMessages.add(message); + } + + public void mergeFrom(TrafficRecorder that) { + for(String sampleMessage: that.getSampleMessages().get()) { + this.sampleMessages.add(sampleMessage); + } + + for(String hourSinceEpoch: that.trafficMapSinceLastSync.keySet()) { + int count = that.trafficMapSinceLastSync.getOrDefault(hourSinceEpoch, 0); + trafficMapSinceLastSync.compute(hourSinceEpoch, (k,v) -> {return (v == null ? 0 : v) + count;}); + } + } + + public boolean isEmpty() { + return trafficMapSinceLastSync.isEmpty(); + } + + public TrafficRecorder(Map trafficMapSinceLastSync) { + this.trafficMapSinceLastSync = trafficMapSinceLastSync; + } + + public Map getTrafficMapSinceLastSync() { + return this.trafficMapSinceLastSync; + } + + public void setTrafficMapSinceLastSync(Map trafficMapSinceLastSync) { + this.trafficMapSinceLastSync = trafficMapSinceLastSync; + } + + public CappedList getSampleMessages() { + return this.sampleMessages; + } + + @Override + public String toString() { + return "{" + + " trafficMapSinceLastSync='" + getTrafficMapSinceLastSync() + "'" + + "}"; + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/type/URLMethods.java b/libs/dao/src/main/java/com/akto/dto/type/URLMethods.java new file mode 100644 index 0000000000..a0d97c4bb7 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/type/URLMethods.java @@ -0,0 +1,23 @@ +package com.akto.dto.type; + +public class URLMethods { + + public enum Method { + GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, PATCH, OTHER; + + private static final Method[] valuesArray = values(); + + public static Method[] getValuesArray () { + return valuesArray; + } + public static Method fromString(String text) { + if (text == null) return OTHER; + for (Method b : Method.values()) { + if (b.name().equalsIgnoreCase(text)) { + return b; + } + } + return OTHER; + } + } +} diff --git a/libs/dao/src/main/java/com/akto/dto/type/URLStatic.java b/libs/dao/src/main/java/com/akto/dto/type/URLStatic.java new file mode 100644 index 0000000000..690871416e --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/type/URLStatic.java @@ -0,0 +1,63 @@ +package com.akto.dto.type; + +import com.akto.dto.type.URLMethods.Method; + +public class URLStatic { + + String url; + Method method; + + public URLStatic() { + } + + public URLStatic(String url, Method method) { + this.url = url; + this.method = method; + } + + public String getFullString() { + return this.url + " " + this.method.name(); + } + + public String getUrl() { + return this.url; + } + + public void setUrl(String url) { + this.url = url; + } + + public Method getMethod() { + return this.method; + } + + public void setMethod(Method method) { + this.method = method; + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof URLStatic)) { + return false; + } + URLStatic uRLStatic = (URLStatic) o; + return this.getFullString().equalsIgnoreCase(uRLStatic.getFullString()); + } + + @Override + public int hashCode() { + return this.getFullString().hashCode(); + } + + @Override + public String toString() { + return "{" + + " url='" + getUrl() + "'" + + ", method='" + getMethod() + "'" + + "}"; + } + + +} diff --git a/libs/dao/src/main/java/com/akto/dto/type/URLTemplate.java b/libs/dao/src/main/java/com/akto/dto/type/URLTemplate.java new file mode 100644 index 0000000000..690dc9e841 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/dto/type/URLTemplate.java @@ -0,0 +1,210 @@ +package com.akto.dto.type; + +import java.util.UUID; + +import com.akto.dao.context.Context; +import com.akto.dto.type.SingleTypeInfo.SuperType; +import com.akto.dto.type.URLMethods.Method; + +import org.bson.codecs.pojo.annotations.BsonDiscriminator; +import org.bson.codecs.pojo.annotations.BsonId; +import org.apache.commons.lang3.math.NumberUtils; + +@BsonDiscriminator +public class URLTemplate { + + @BsonId + String id; + int creationTs; + int lastUpdateTs; + String[] tokens; + SuperType[] types; + Method method; + + public URLTemplate() { + } + + public URLTemplate(String[] tokens, SuperType[] types, Method method) { + this.tokens = tokens; + this.types = types; + this.id = UUID.randomUUID().toString(); + this.creationTs = Context.now(); + this.lastUpdateTs = creationTs; + this.method = method; + } + + public boolean match(String url, Method urlMethod) { + if (url.startsWith("/")) url = url.substring(1, url.length()); + if (url.endsWith("/")) url = url.substring(0, url.length()-1); + + String tempUrl = this.getTemplateString(); + if (tempUrl.startsWith("/")) tempUrl = tempUrl.substring(1, tempUrl.length()); + if (tempUrl.endsWith("/")) tempUrl = tempUrl.substring(0, tempUrl.length()-1); + + String a = url + " " + urlMethod.name(); + String b = tempUrl + " " + this.getMethod().name(); + if (a.equals(b)) { + return true; + } + + String[] thatTokens = url.split("/"); + + return match(thatTokens, urlMethod); + } + + public boolean match(URLStatic urlStatic) { + return this.match(urlStatic.getUrl(), urlStatic.getMethod()); + } + + public boolean match(String[] url, Method urlMethod) { + if (urlMethod != method) { + return false; + } + String[] thatTokens = url; + if (thatTokens.length != this.tokens.length) return false; + + for (int i = 0; i < thatTokens.length; i++) { + String thatToken = thatTokens[i]; + String thisToken = this.tokens[i]; + + if (thisToken == null) { + SuperType type = types[i]; + + switch(type) { + case BOOLEAN: + if (!"true".equals(thatToken.toLowerCase()) && !"false".equals(thatToken.toLowerCase())) return false; + break; + case INTEGER: + if (!NumberUtils.isParsable(thatToken) || thatToken.contains(".")) return false; + break; + case FLOAT: + if (!NumberUtils.isParsable(thatToken) || !thatToken.contains(".")) return false; + break; + default: + continue; + + } + + } else { + if (!thisToken.equals(thatToken)) { + return false; + } + } + } + + return true; + } + + public String getTemplateString() { + String str = ""; + for(int i = 0;i < tokens.length; i++) { + if (i > 0) { + str += "/"; + } + if (tokens[i] == null) { + str += types[i].name(); + } else { + str += tokens[i]; + } + } + return str; + } + + public String[] getTokens() { + return this.tokens; + } + + public void setTokens(String[] tokens) { + this.tokens = tokens; + } + + public SuperType[] getTypes() { + return this.types; + } + + public void setTypes(SuperType[] types) { + this.types = types; + } + + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + public int getCreationTs() { + return this.creationTs; + } + + public void setCreationTs(int creationTs) { + this.creationTs = creationTs; + } + + public int getLastUpdateTs() { + return this.lastUpdateTs; + } + + public void setLastUpdateTs(int lastUpdateTs) { + this.lastUpdateTs = lastUpdateTs; + } + + public Method getMethod() { + return this.method; + } + + public void setMethod(Method method) { + this.method = method; + } + + @Override + public String toString() { + return "{" + + " tokens='" + getTokens() + "'" + + ", types='" + getTypes() + "'" + + ", id='" + getId() + "'" + + ", creationTs='" + getCreationTs() + "'" + + ", lastUpdateTs='" + getLastUpdateTs() + "'" + + ", method='" + getMethod() + "'" + + "}"; + } + + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof URLTemplate)) { + return false; + } + + URLTemplate that = (URLTemplate) o; + + if (that.getMethod() != this.getMethod()) { + return false; + } + + for(int i = 0; i < tokens.length; i ++) { + if(that.tokens[i] == null ? ( this.types[i]==null || this.types[i] != that.types[i] ) : ( this.tokens[i]==null || !this.tokens[i].equals(that.tokens[i]) ) ){ + return false; + } + } + + return true; + } + + @Override + public int hashCode() { + int ret = 0; + for(int i = 0; i < tokens.length; i ++) { + if(this.tokens[i] == null) { + ret += this.types[i].hashCode(); + } else { + ret += this.tokens[i].hashCode(); + } + } + + return ret + method.hashCode(); + } +} diff --git a/libs/dao/src/main/java/com/akto/types/BasicDBListL.java b/libs/dao/src/main/java/com/akto/types/BasicDBListL.java new file mode 100644 index 0000000000..c85994ff7a --- /dev/null +++ b/libs/dao/src/main/java/com/akto/types/BasicDBListL.java @@ -0,0 +1,18 @@ +package com.akto.types; + +import com.mongodb.BasicDBList; + +public class BasicDBListL extends BasicDBList { + + public BasicDBListL() { + super(); + } + + public BasicDBListL(Object... args){ + super(); + for(Object arg: args) { + this.add(arg); + } + } + +} diff --git a/libs/dao/src/main/java/com/akto/types/CappedList.java b/libs/dao/src/main/java/com/akto/types/CappedList.java new file mode 100644 index 0000000000..9dfe62ae81 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/types/CappedList.java @@ -0,0 +1,79 @@ +package com.akto.types; + +import java.util.ArrayList; + +public class CappedList { + + int limit; + ArrayList elements; + boolean forceLatestEntry; + int nextPos; + + public CappedList() { + } + + public CappedList(int limit, boolean forceLatestEntry) { + this.limit = limit; + this.forceLatestEntry = forceLatestEntry; + this.elements = new ArrayList(); + this.nextPos = 0; + } + + public boolean add(T t) { + boolean isInserted = false; + + if (elements.indexOf(t) != -1) { + return false; + } + + if (elements.size() < limit) { + this.elements.add(t); + isInserted = true; + } else if (forceLatestEntry) { + this.elements.set(nextPos, t); + isInserted = true; + } + + if (isInserted) { + this.nextPos = ((this.nextPos + 1) % limit) % limit; + } + + return isInserted; + } + + public ArrayList get() { + return this.elements; + } + + public int getLimit() { + return limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public ArrayList getElements() { + return elements; + } + + public void setElements(ArrayList elements) { + this.elements = elements; + } + + public boolean isForceLatestEntry() { + return forceLatestEntry; + } + + public void setForceLatestEntry(boolean forceLatestEntry) { + this.forceLatestEntry = forceLatestEntry; + } + + public int getNextPos() { + return nextPos; + } + + public void setNextPos(int nextPos) { + this.nextPos = nextPos; + } +} diff --git a/libs/dao/src/main/java/com/akto/types/CappedSet.java b/libs/dao/src/main/java/com/akto/types/CappedSet.java new file mode 100644 index 0000000000..3c04da2883 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/types/CappedSet.java @@ -0,0 +1,44 @@ +package com.akto.types; + +import java.util.HashSet; +import java.util.Set; + +public class CappedSet { + + public static final int LIMIT = 50; + + public static CappedSet create(T elem) { + CappedSet ret = new CappedSet(); + ret.add(elem); + return ret; + } + + Set elements; + + public CappedSet(Set elements) { + this.elements = elements; + } + + public CappedSet() { + this(new HashSet<>()); + } + + public int count() { + if (this.elements == null) return 0; + return this.elements.size(); + } + + public boolean add(T t) { + if (elements.size() >= LIMIT) return false; + elements.add(t); + return true; + } + + public Set getElements() { + return elements; + } + + public void setElements(Set elements) { + this.elements = elements; + } +} diff --git a/libs/dao/src/main/java/com/akto/util/Constants.java b/libs/dao/src/main/java/com/akto/util/Constants.java new file mode 100644 index 0000000000..f3217b2fb6 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/Constants.java @@ -0,0 +1,12 @@ +package com.akto.util; + +public class Constants { + private Constants() {} + + public static final String ID = "_id"; + + public static final String AWS_REGION = "AWS_REGION"; + + public static final String AWS_ACCOUNT_ID = "AWS_ACCOUNT_ID"; + +} diff --git a/libs/dao/src/main/java/com/akto/util/DateUtils.java b/libs/dao/src/main/java/com/akto/util/DateUtils.java new file mode 100644 index 0000000000..9a89ddf684 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/DateUtils.java @@ -0,0 +1,13 @@ +package com.akto.util; + +public class DateUtils { + + public enum TrackingPeriod { + DAILY, WEEKLY, MONTHLY, QUARTERLY, HALF_YEARLY, YEARLY; + + public static DateUtils.TrackingPeriod getTrackingPeriod(int trackingPeriod) { + return values()[trackingPeriod]; + } + + } +} diff --git a/libs/dao/src/main/java/com/akto/util/EnumCodec.java b/libs/dao/src/main/java/com/akto/util/EnumCodec.java new file mode 100644 index 0000000000..b51017746f --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/EnumCodec.java @@ -0,0 +1,30 @@ +package com.akto.util; + +import org.bson.BsonReader; +import org.bson.BsonWriter; +import org.bson.codecs.Codec; +import org.bson.codecs.DecoderContext; +import org.bson.codecs.EncoderContext; + +public final class EnumCodec> implements Codec { + private final Class clazz; + + public EnumCodec(final Class clazz) { + this.clazz = clazz; + } + + @Override + public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) { + writer.writeString(value.name()); + } + + @Override + public Class getEncoderClass() { + return clazz; + } + + @Override + public T decode(final BsonReader reader, final DecoderContext decoderContext) { + return Enum.valueOf(clazz, reader.readString()); + } +} diff --git a/libs/dao/src/main/java/com/akto/util/JSONUtils.java b/libs/dao/src/main/java/com/akto/util/JSONUtils.java new file mode 100644 index 0000000000..c83631557b --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/JSONUtils.java @@ -0,0 +1,182 @@ +package com.akto.util; + +import java.util.*; + +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.type.RequestTemplate; +import com.akto.util.modifier.PayloadModifier; +import com.google.gson.Gson; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import org.json.JSONArray; + +public class JSONUtils { + private static void flatten(Object obj, String prefix, Map> ret) { + if (obj instanceof BasicDBObject) { + BasicDBObject basicDBObject = (BasicDBObject) obj; + + Set keySet = basicDBObject.keySet(); + + if (prefix != null && !prefix.isEmpty() && (keySet == null || keySet.isEmpty())) { + Set values = ret.getOrDefault(prefix, new HashSet<>()); + values.add(obj); + ret.put(prefix, values); + } + + for(String key: keySet) { + + if (key == null) { + continue; + } + boolean anyAlphabetExists = false; + + final int sz = key.length(); + for (int i = 0; i < sz; i++) { + final char nowChar = key.charAt(i); + if (Character.isLetter(nowChar)) { + anyAlphabetExists = true; + break; + } + } + + key = anyAlphabetExists ? key: "NUMBER"; + Object value = basicDBObject.get(key); + flatten(value, prefix + (prefix.isEmpty() ? "" : "#") + key, ret); + } + } else if (obj instanceof BasicDBList) { + for(Object elem: (BasicDBList) obj) { + flatten(elem, prefix+(prefix.isEmpty() ? "$" : "#$"), ret); + } + } else { + Set values = ret.getOrDefault(prefix, new HashSet<>()); + values.add(obj); + ret.put(prefix, values); + } + } + + public static Map> flatten(BasicDBObject object) { + Map> ret = new HashMap<>(); + if (object == null) return ret; + String prefix = ""; + flatten(object, prefix, ret); + return ret; + } + + public static BasicDBObject flattenWithDots(Object object) { + BasicDBObject ret = new BasicDBObject(); + String prefix = ""; + flattenWithDots(object, prefix, ret); + return ret; + } + + private static void flattenWithDots(Object obj, String prefix, BasicDBObject ret) { + if (obj instanceof BasicDBObject) { + BasicDBObject basicDBObject = (BasicDBObject) obj; + + Set keySet = basicDBObject.keySet(); + + if (prefix != null && !prefix.isEmpty() && (keySet == null || keySet.isEmpty())) { + ret.put(prefix, obj); + } + + for(String key: keySet) { + + if (key == null) { + continue; + } + boolean anyAlphabetExists = false; + + final int sz = key.length(); + for (int i = 0; i < sz; i++) { + final char nowChar = key.charAt(i); + if (Character.isLetter(nowChar)) { + anyAlphabetExists = true; + break; + } + } + + key = anyAlphabetExists ? key: "NUMBER"; + Object value = basicDBObject.get(key); + flattenWithDots(value, prefix + (prefix == null || prefix.isEmpty() ? "" : ".") + key, ret); + } + } else if (obj instanceof BasicDBList) { + int idx = 0; + for(Object elem: (BasicDBList) obj) { + flattenWithDots(elem, prefix+("["+idx+"]"), ret); + idx += 1; + } + } else { + ret.put(prefix, obj); + } + } + + public static String modify(String jsonBody, Set values, PayloadModifier payloadModifier) { + try { + BasicDBObject payload = RequestTemplate.parseRequestPayload(jsonBody, null); + if (payload.isEmpty()) return null; + BasicDBObject modifiedPayload = modify(payload, values, payloadModifier); + if (modifiedPayload.containsKey("json")) { + return new Gson().toJson(modifiedPayload.get("json")); + } + return new Gson().toJson(modifiedPayload); + } catch (Exception e) { + ; + return null; + } + } + + public static BasicDBObject modify(BasicDBObject obj, Set values, PayloadModifier payloadModifier) { + BasicDBObject result = (BasicDBObject) obj.copy(); + modify(result, "" ,values, payloadModifier); + return result; + } + + private static void modify(Object obj, String prefix, Set values, PayloadModifier payloadModifier) { + if (obj instanceof BasicDBObject) { + BasicDBObject basicDBObject = (BasicDBObject) obj; + Set keySet = basicDBObject.keySet(); + + for(String key: keySet) { + if (key == null) continue; + String fullKey = prefix + (prefix.isEmpty() ? "" : "#") + key; + Object value = basicDBObject.get(key); + if (values.contains(fullKey)) { + basicDBObject.put(key, payloadModifier.modify(key, value)); + } + modify(value, fullKey, values, payloadModifier); + } + + } else if (obj instanceof BasicDBList) { + for (Object elem: (BasicDBList) obj) { + modify(elem, prefix+(prefix.isEmpty() ? "$" : "#$"), values, payloadModifier); + } + } + } + + + public static Map> modifyHeaderValues(Map> headers, PayloadModifier payloadModifier) { + if (headers == null) return null; + boolean flag = false; + Map> modifiedHeaders = new HashMap<>(headers); + for (String header: modifiedHeaders.keySet()) { + List values = modifiedHeaders.get(header); + List newValues = new ArrayList<>(); + for (String value: values) { + Object modifiedHeader = payloadModifier.modify(header, value); + if (modifiedHeader != null) { + newValues.add(modifiedHeader.toString()); + flag = true; + } else { + newValues.add(value); + } + } + modifiedHeaders.put(header, newValues); + } + + if (!flag) return null; + + return modifiedHeaders; + } + + +} diff --git a/libs/dao/src/main/java/com/akto/util/JsonStringPayloadModifier.java b/libs/dao/src/main/java/com/akto/util/JsonStringPayloadModifier.java new file mode 100644 index 0000000000..61c8631b29 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/JsonStringPayloadModifier.java @@ -0,0 +1,34 @@ +package com.akto.util; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +public class JsonStringPayloadModifier { + private static final ObjectMapper mapper = new ObjectMapper(); + + public static String jsonStringPayloadModifier(String data, String path, String newVal) throws Exception { + + try { + JsonNode origRequestNode = mapper.readValue(data, JsonNode.class); + JsonNode node = origRequestNode; + JsonNode parentNode = origRequestNode; + String []keys = path.split("\\."); + for (int i=0; i { + + private U first; + private V second; + + public Pair() { + } + + public Pair(U first, V second) { + this.first = first; + this.second = second; + } + + public U getFirst() { + return this.first; + } + + public void setFirst(U first) { + this.first = first; + } + + public V getSecond() { + return this.second; + } + + public void setSecond(V second) { + this.second = second; + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Pair)) { + return false; + } + Pair pair = (Pair) o; + return Objects.equals(first, pair.first) && Objects.equals(second, pair.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } + + @Override + public String toString() { + return "{" + + " first='" + getFirst() + "'" + + ", second='" + getSecond() + "'" + + "}"; + } + + + +} diff --git a/libs/dao/src/main/java/com/akto/util/RecordedLoginFlowUtil.java b/libs/dao/src/main/java/com/akto/util/RecordedLoginFlowUtil.java new file mode 100644 index 0000000000..4358e213f3 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/RecordedLoginFlowUtil.java @@ -0,0 +1,138 @@ +package com.akto.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.concurrent.TimeUnit; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.FileUtils; +import org.bson.conversions.Bson; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.akto.dao.RecordedLoginInputDao; +import com.akto.dao.context.Context; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.Gson; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Updates; +import com.akto.ApiRequest; +import com.akto.TimeoutObject; + +public class RecordedLoginFlowUtil { + + private static final Gson gson = new Gson(); + + private static final Logger logger = LoggerFactory.getLogger(RecordedLoginFlowUtil.class); + + public static void triggerFlow(String tokenFetchCommand, String payload, String outputFilePath, String errorFilePath, int userId) throws Exception { + + try { + String url = System.getenv("PUPPETEER_REPLAY_SERVICE_URL"); + JSONObject requestBody = new JSONObject(); + requestBody.put("replayJson", payload); + requestBody.put("command", tokenFetchCommand); + + String reqData = requestBody.toString(); + TimeoutObject timeoutObj = new TimeoutObject(300, 300, 300); + JsonNode node = ApiRequest.postRequestWithTimeout(new HashMap<>(), url, reqData, timeoutObj); + + String token = node.get("token").textValue(); + + FileUtils.writeStringToFile(new File(outputFilePath), token, (String) null); + + if (userId != 0) { + Bson filter = Filters.and( + Filters.eq("userId", userId) + ); + Bson update = Updates.combine( + Updates.set("content", payload), + Updates.set("tokenFetchCommand", tokenFetchCommand), + Updates.setOnInsert("createdAt", Context.now()), + Updates.set("updatedAt", Context.now()), + Updates.set("outputFilePath", outputFilePath), + Updates.set("errorFilePath", errorFilePath) + ); + RecordedLoginInputDao.instance.updateOne(filter, update); + } + + + } catch (Exception e) { + FileUtils.writeStringToFile(new File(errorFilePath), e.getMessage(), (String) null); + + if (userId != 0) { + Bson filter = Filters.and( + Filters.eq("userId", userId) + ); + Bson update = Updates.combine( + Updates.set("content", payload), + Updates.set("tokenFetchCommand", tokenFetchCommand), + Updates.setOnInsert("createdAt", Context.now()), + Updates.set("updatedAt", Context.now()), + Updates.set("outputFilePath", outputFilePath), + Updates.set("errorFilePath", errorFilePath) + ); + RecordedLoginInputDao.instance.updateOne(filter, update); + } + + logger.error("error executing recorded login flow " + e.getMessage()); + throw new Exception("error executing recorded login flow " + e.getMessage()); + } + + } + + public static String fetchToken(String outputFilePath, String errorFilePath) throws Exception { + + String fileContent; + + try { + FileInputStream fstream = new FileInputStream(errorFilePath); + + String error = null; + + try (BufferedReader br = new BufferedReader(new InputStreamReader(fstream))) { + String line; + while ((line = br.readLine()) != null) { + if((line.toLowerCase().contains("error") || line.toLowerCase().contains("failed")) || line.toLowerCase().contains("timeout")){ + error = line; + break; + } + } + } + + if (error != null) { + throw new Exception("error executing recording " + error); + } + + } catch (IOException e) { + logger.error("error processing file operations " + e.getMessage()); + throw new Exception("error fetching token data, " + e.getMessage()); + } + try { + fileContent = FileUtils.readFileToString(new File(outputFilePath), StandardCharsets.UTF_8); + + if (fileContent == null) { + throw new Exception("token not received, found null"); + } + + return fileContent.toString(); + + } catch (IOException e) { + logger.error("error processing file operations " + e.getMessage()); + throw new Exception("error fetching token data, " + e.getMessage()); + } catch (Exception e) { + logger.error("error fetching recorded flow output " + e.getMessage()); + throw new Exception("token data not found, " + e.getMessage()); + } + } +} + diff --git a/libs/dao/src/main/java/com/akto/util/TokenPayloadModifier.java b/libs/dao/src/main/java/com/akto/util/TokenPayloadModifier.java new file mode 100644 index 0000000000..2fe6577c48 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/TokenPayloadModifier.java @@ -0,0 +1,29 @@ +package com.akto.util; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.testing.AuthParam; + +public class TokenPayloadModifier { + + public static Boolean tokenPayloadModifier(OriginalHttpRequest request, String key, String value, AuthParam.Location where) { + if (where.toString().equals(AuthParam.Location.BODY.toString())) { + try { + String resp = JsonStringPayloadModifier.jsonStringPayloadModifier(request.getBody(), key, value); + request.setBody(resp); + } catch(Exception e) { + return false; + } + } + else { + Map> headers = request.getHeaders(); + String k = key.toLowerCase().trim(); + if (!headers.containsKey(k)) return false; + headers.put(k, Collections.singletonList(value)); + } + return true; + } +} diff --git a/libs/dao/src/main/java/com/akto/util/Trie.java b/libs/dao/src/main/java/com/akto/util/Trie.java new file mode 100644 index 0000000000..637ff791e1 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/Trie.java @@ -0,0 +1,145 @@ +package com.akto.util; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import com.akto.dto.type.KeyTypes; + +public class Trie { + + public static class Node { + public static Node createTerminal(K pathElem, V value) { + return new Node(pathElem, value, new HashMap<>()); + } + + K pathElem; + V value; + Map, Node> children; + + private Node(K pathElem, V value, Map, Node> children) { + this.pathElem = pathElem; + this.value = value; + this.children = children; + } + + public Node getOrCreate(K name, V defaultValue) { + Node key = Node.createTerminal(name, defaultValue); + Node ret = children.get(key); + + if (ret == null) { + ret = key; + children.put(key, key); + } + + return ret; + } + + public Node get(K name) { + Node key = Node.createTerminal(name, null); + return children.get(key); + } + + public void mergeFrom(Node that, BiConsumer mergeKeyFunc) { + if (that.children != null) { + for(Node thatChild: that.children.keySet()) { + Node thisChild = this.children.get(thatChild.getPathElem()); + if (thisChild == null) { + this.children.put(thatChild, thatChild); + } else { + thisChild.mergeFrom(thatChild, mergeKeyFunc); + } + } + } + + mergeKeyFunc.accept(this.getValue(), that.getValue()); + } + + public K getPathElem() { + return this.pathElem; + } + + public V getValue() { + return this.value; + } + + public Map, Node> getChildren() { + return this.children; + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Node)) { + return false; + } + Node node = (Node) o; + return pathElem.equals(node.getPathElem()); + } + + @Override + public int hashCode() { + return pathElem.hashCode(); + } + + public void print(Consumer> consumer) { + printHelper(0, consumer); + } + + void printHelper(int tab, Consumer> consumer) { + for(int i = 0; i < tab; i++) { + System.out.print("\t"); + } + + if (consumer != null) consumer.accept(this); + for(Node node: children.values()) { + node.printHelper(tab+1, consumer); + } + } + } + + Node>> root; + + public Trie() { + root = Node.createTerminal("", new Pair>(new KeyTypes(new HashMap<>(), false), new HashSet())); + } + + public Node>> getRoot() { + return this.root; + } + + public void flatten(Map parameters) { + + for (Node>> node: root.children.keySet()) { + flattenHelper(node, parameters, ""); + } + } + + private void flattenHelper(Node>> node, Map parameters, String prefix) { + prefix += ("#"+node.pathElem); + + if (prefix.startsWith("#")) { + prefix = prefix.substring(1); + } + + KeyTypes keyTypes = node.value.getFirst(); + if (keyTypes != null && !keyTypes.getOccurrences().isEmpty()) { + KeyTypes curr = parameters.get(prefix); + if (curr == null) { + parameters.put(prefix, keyTypes.copy()); + } else { + curr.getOccurrences().putAll(keyTypes.getOccurrences()); + } + } + + if (node.children != null && !node.children.isEmpty()) { + for(Node>> child: node.getChildren().keySet()) { + flattenHelper(child, parameters, prefix); + } + } + } +} diff --git a/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java b/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java new file mode 100644 index 0000000000..2d340146b4 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/enums/GlobalEnums.java @@ -0,0 +1,435 @@ +package com.akto.util.enums; + +public class GlobalEnums { + /* * * * * * * * Enums for Testing run issues * * * * * * * * * * * * */ + + public enum TestErrorSource { // Whether issue came from runtime or automated testing via dashboard + AUTOMATED_TESTING("testing"), + RUNTIME("runtime"); + + private final String name; + + TestErrorSource(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + /* Category of tests perfomred */ + public enum TestCategory { + BOLA("BOLA", Severity.HIGH, "Broken Object Level Authorization (BOLA)", "BOLA"), + NO_AUTH("NO_AUTH", Severity.HIGH, "Broken User Authentication (BUA)", "Broken Authentication"), + BFLA("BFLA", Severity.HIGH, "Broken Function Level Authorization (BFLA)", "Broken Function Level Authorization"), + IAM("IAM", Severity.HIGH, "Improper Assets Management (IAM)", "Improper Assets Management"), + EDE("EDE", Severity.HIGH, "Excessive Data Exposure (EDE)", "Sensitive Data Exposure"), + RL("RL", Severity.HIGH, "Lack of Resources & Rate Limiting (RL)", "Lack of Resources and Rate Limiting"), + MA("MA", Severity.HIGH, "Mass Assignment (MA)", "Mass Assignment"), + INJ("INJ", Severity.HIGH, "Injection (INJ)", "Injection"), + ILM("ILM", Severity.HIGH, "Insufficient Logging & Monitoring (ILM)", "Insufficient Logging and Monitoring"), + SM("SM", Severity.HIGH, "Security Misconfiguration (SM)", "Misconfiguration"), + UC("UC", Severity.HIGH, "Uncategorized (UC)", "Uncategorized"); + private final String name; + private final Severity severity; + private final String displayName; + private final String shortName; + + TestCategory(String name, Severity severity, String displayName, String shortName) { + this.name = name; + this.severity = severity; + this.displayName = displayName; + this.shortName = shortName; + } + + public String getName() { + return name; + } + + public Severity getSeverity() { + return severity; + } + + public String getDisplayName() { + return displayName; + } + + public String getShortName() { + return shortName; + } + } + + public enum IssueTags { + BL("Business logic"), + OWASPTOP10("OWASP top 10"), + HACKERONETOP10("HackerOne top 10"); + private final String name; + IssueTags(String name) { + this.name = name; + } + public String getName() { + return name; + } + } + + public enum TestSubCategory { + REPLACE_AUTH_TOKEN( + "REPLACE_AUTH_TOKEN", + TestCategory.BOLA, + "BOLA by changing auth token", + "Attacker can access resources of any user by changing the auth token in request.", + "The endpoint appears to be vulnerable to broken object level authorization attack. The original request " + + "was replayed with attacker's auth token. The server responded with 2XX success codes and greater" + + " than {{percentageMatch}}% of the response body matched with original response body. Also, the endpoint had atleast one private " + + "resources in request payload.

" + + "Background: Object level authorization is an access control mechanism that is usually implemented at the code level to" + + " validate that one user can only access objects that they should have access to.", + "Unauthorized access can result in data disclosure to unauthorized parties, data loss, or data manipulation. Unauthorized access to objects can also lead to full account takeover.", + new String[]{ + "https://www.akto.io/blog/bola-exploitation-using-unauthorized-uuid-on-api-endpoint", + "https://www.akto.io/blog/what-is-broken-object-level-authorization-bola", + "https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa1-broken-object-level-authorization.md", + "https://cwe.mitre.org/data/definitions/284.html", + "https://cwe.mitre.org/data/definitions/285.html", + "https://cwe.mitre.org/data/definitions/639.html" + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + + ADD_USER_ID( + "ADD_USER_ID", + TestCategory.BOLA, + "IDOR by adding user id in query params", + "Attacker can access resources of any user by adding user_id in URL.", + "The endpoint appears to be vulnerable to broken object level authorization attack. The original request was replayed by adding other user's user id in query params. " + + "The server responded with 2XX success codes and less than {{percentageMatch}}% of the response body matched with original response body.

" + + "Background: Object level authorization is an access control mechanism that is usually implemented at the code level to validate that one user can only access objects that they should have access to.", + "Unauthorized access can result in data disclosure to unauthorized parties, data loss, or data manipulation. Unauthorized access to objects can also lead to full account takeover.", + new String[]{ + "https://www.akto.io/blog/bola-exploitation-using-unauthorized-uuid-on-api-endpoint", + "https://www.akto.io/blog/what-is-broken-object-level-authorization-bola", + "https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa1-broken-object-level-authorization.md", + "https://cwe.mitre.org/data/definitions/284.html", + "https://cwe.mitre.org/data/definitions/285.html", + "https://cwe.mitre.org/data/definitions/639.html" + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + ADD_METHOD_IN_PARAMETER( + "ADD_METHOD_IN_PARAMETER", + TestCategory.BFLA, + "BFLA by HTTP verb tunneling by adding method param", + "Attacker can access resources of any user by replacing method of the endpoint (eg: changemethod from get to post). This way attacker can get access to unauthorized endpoints.", + "The endpoint appears to be vulnerable to broken function level authorization attack. The original request was replayed with the addition of request parameter " + + "(for example: {method = GET} was added to change it to GET method) in query params." + + " The application responded with 2XX success codes and the response body match was less than {{percentageMatch}}%.

" + + "Background: Some web frameworks provide a way to override the actual HTTP method in the request by emulating the missing HTTP verbs passing some custom parameter in query params. " + + "The main purpose of this is to circumvent some middleware (e.g. proxy, firewall) limitation where methods allowed usually do not encompass verbs such as PUT or DELETE.", + "An attacker can perform sensitive actions (e.g., creation, modification, or erasure) that they should not have access to by simply overriding the HTTP method by adding parameter method in query params.", + new String[]{ + "https://owasp.org/www-project-web-security-testing-guide/v42/4-Web_Application_Security_Testing/02-Configuration_and_Deployment_Management_Testing/06-Test_HTTP_Methods", + "https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa5-broken-function-level-authorization.md", + "https://cwe.mitre.org/data/definitions/285.html", + "https://fanoframework.github.io/security/http-verb-tunnelling/#:~:text=What%20is%20it%3F,allows%20GET%20and%20POST%20request.&text=Alternatively%2C%20you%20can%20also%20use,body%20parameter%20_method%20like%20so." + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + ADD_METHOD_OVERRIDE_HEADERS( + "ADD_METHOD_OVERRIDE_HEADERS", + TestCategory.BFLA, + "BFLA by HTTP method overriding ", + "Attacker can access resources of any user by replacing method of the endpoint (eg: changemethod from get to post). This way attacker can get access to unauthorized endpoints.", + "The endpoint appears to be vulnerable to broken function level authorization attack.The original request was replayed with addition of alternative headers" + + " for HTTP method overriding namely X-HTTP-Method, X-HTTP-Method-Override, X-Method-Override. The application responded with 2XX success codes and less than " + + "{{percentageMatch}}% of the response body matched with original response body.

" + + + "Background: Some web frameworks provide a way to override the actual HTTP method in the request by emulating the missing HTTP verbs passing some custom header in the requests. " + + "The main purpose of this is to circumvent some middleware (e.g. proxy, firewall) limitation where methods allowed usually do not encompass verbs such as PUT or DELETE. The following alternative" + + " headers could be used to do such verb tunneling: X-HTTP-Method, X-HTTP-Method-Override, X-Method-Override", + "An attacker can perform sensitive actions (e.g., creation, modification, or erasure) that they should not have access to by simply overriding the HTTP method.", + new String[]{ + "https://owasp.org/www-project-web-security-testing-guide/v42/4-Web_Application_Security_Testing/02-Configuration_and_Deployment_Management_Testing/06-Test_HTTP_Methods", + "https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa5-broken-function-level-authorization.md", + "https://cwe.mitre.org/data/definitions/285.html" + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + CHANGE_METHOD( + "CHANGE_METHOD", + TestCategory.BFLA, + "BFLA by changing HTTP method", + "Attacker can access resources of any user by replacing method of the endpoint (eg: changemethod from get to post). This way attacker can get access to unauthorized endpoints.", + "The endpoint appears to be vulnerable to broken function level authorization attack. The original request was modified by changing the HTTP methods and sent to application server. The server responded with 2XX success codes.", + "An attacker can perform sensitive actions (e.g., creation, modification, or erasure) that they should not have access to by simply changing the HTTP method.", + new String[]{ + "https://owasp.org/www-project-web-security-testing-guide/v42/4-Web_Application_Security_Testing/02-Configuration_and_Deployment_Management_Testing/06-Test_HTTP_Methods", + "https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa5-broken-function-level-authorization.md", + "https://cwe.mitre.org/data/definitions/285.html" + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + REMOVE_TOKENS( + "REMOVE_TOKENS", + TestCategory.NO_AUTH, + "Broken Authentication by removing auth token", + "API doesn't validate the authenticity of token. Attacker can remove the auth token and access the endpoint.", + "The endpoint appears to be vulnerable to broken authentication attack. The original request was replayed by removing victim's auth token. The server responded with 2XX success codes.

" + + "Background: Authentication is the process of attempting to verify the digital identity of the sender of a communication. Testing the authentication schema means understanding how the authentication process works and using that information to " + + "circumvent the authentication mechanism. While most applications require authentication to gain access to private information or to execute tasks, not every authentication method is able to provide adequate security. Negligence, ignorance, or simple " + + "understatement of security threats often result in authentication schemes that can be bypassed by simply skipping the log in page and directly calling an internal page that is supposed to be accessed only after authentication has been performed.", + "Broken User authentication is a serious vulnerability. Attackers can gain control to other users’ accounts in the system, read their personal data, and perform sensitive actions on their behalf, like money transactions and sending personal messages.", + new String[]{ + "https://owasp.org/www-project-web-security-testing-guide/v42/4-Web_Application_Security_Testing/", + "https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa2-broken-user-authentication.md", + "https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html", + "https://cwe.mitre.org/data/definitions/798.html" + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + PARAMETER_POLLUTION( + "PARAMETER_POLLUTION", + TestCategory.BOLA, + "BOLA by HTTP Parameter Pollution", + "Attacker can access resources of any user by introducing multiple parameters with same name.", + "The endpoint appears to be vulnerable to broken object level authorization attack. The original request was replayed by adding private resources in query params (for eg: user_id=1234&account_id=436783).. " + + " The server responded with 2XX success codes and less than {{percentageMatch}}% of the response body matched with original response body.

" + + "Background: Object level authorization is an access control mechanism that is usually implemented at the code level to validate that one user can only access objects that they should have access to.", + "Unauthorized access can result in data disclosure to unauthorized parties, data loss, or data manipulation. Unauthorized access to objects can also lead to full account takeover.", + new String[]{ + "https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/04-Testing_for_HTTP_Parameter_Pollution", + "https://www.madlab.it/slides/BHEU2011/whitepaper-bhEU2011.pdf", + "https://www.akto.io/blog/what-is-broken-object-level-authorization-bola", + "https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa1-broken-object-level-authorization.md", + "https://cwe.mitre.org/data/definitions/284.html" + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + REPLACE_AUTH_TOKEN_OLD_VERSION( + "REPLACE_AUTH_TOKEN_OLD_VERSION", + TestCategory.BOLA, + "BOLA in old api versions", + "Attacker can access resources of any user by changing the auth token in request and using older version of an API", + "Unpatched old api versions were found for this endpoint. The original request was replayed by changing the version of the endpoint " + + "(For example: the request url was changed from www.example.com/dfsh/v5/fd to www.example.com/dfsh/v1/fd). The server responded with 2XX success codes.

" + + "The old API versions found appears to be vulnerable to broken object level authorization attack. The original " + + "request was replayed with attacker's auth token. The server responded with 2XX success codes and greater than {{percentageMatch}}% of the response body matched with original" + + " response body. Also, the endpoint had atleast one private resources in request payload.

" + + "Background: Old API versions are usually unpatched and are vulnerable to attacks such as BOLA. Object level authorization is an access control mechanism that is usually" + + " implemented at the code level to validate that one user can only access objects that they should have access to.", + "Unauthorized access can result in data disclosure to unauthorized parties, data loss, or data manipulation. Unauthorized access to objects can also lead to full account takeover.", + new String[]{ + "https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa9-improper-assets-management.md", + "https://www.akto.io/blog/bola-exploitation-using-unauthorized-uuid-on-api-endpoint", + "https://www.akto.io/blog/what-is-broken-object-level-authorization-bola", + "https://github.com/OWASP/API-Security/blob/master/2019/en/src/0xa1-broken-object-level-authorization.md", + "https://cwe.mitre.org/data/definitions/284.html", + "https://cwe.mitre.org/data/definitions/285.html", + "https://cwe.mitre.org/data/definitions/639.html" + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + JWT_NONE_ALGO( + "JWT_NONE_ALGO", + TestCategory.NO_AUTH, + "JWT None Algorithm", + "Since NONE Algorithm JWT is accepted by the server the attacker can tamper with the payload of JWT and access protected resources.", + "The endpoint appears to be vulnerable to broken authentication attack.The original request was replayed by changing algorithm of JWT token " + + " to NONE in request headers. The server responded with 2XX success codes. This indicates that this endpoint can be accessed without JWT signature which means a malicious user can get unauthorized access to this endpoint.

" + + "Background: All JSON Web Tokens should contain the \"alg\" header parameter, which specifies the algorithm that the server should use to verify the signature of the token. In addition to cryptographically strong algorithms, " + + "the JWT specification also defines the \"none\" algorithm, which can be used with \"unsecured\" (unsigned) JWTs. When this algorithm is supported on the server, it may accept tokens that have no signature at all.

" + + "As the JWT header can be tampered with client-side, a malicious user could change the \"alg\" header to \"none\", then remove the signature and check whether the server still accepts the token.", + "If JWT none algorithm works, attacker can do a full account takeover." + + "They can also exploit this vulnerability by supplying an arbitrary claim in the JWT payload to escalate their privileges or impersonate other users. For example, if the token contains a \"username\": \"joe\" claim, they could change this to \"username\": \"admin\".", + new String[]{ + "https://redhuntlabs.com/a-practical-guide-to-attack-jwt-json-web-token", + "https://portswigger.net/kb/issues/00200901_jwt-none-algorithm-supported" + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + BFLA( + "BFLA", + TestCategory.BFLA, + "BFLA", + "Less privileged attacker can access admin resources", + "", + "", + new String[]{ + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + JWT_INVALID_SIGNATURE( + "JWT_INVALID_SIGNATURE", + TestCategory.NO_AUTH, + "JWT Failed to verify Signature", + "Since server is not validating the JWT signature the attacker can tamper with the payload of JWT and access protected resources", + "The endpoint appears to be vulnerable to broken authentication attack.The original request was replayed by changing the valid signature" + + " of JWT to invalid signature. The server responded with 2XX success codes. This indicates that this endpoint can be " + + "accessed with an invalid JWT signature as the developer has failed to properly verify the signature with every request.

" + + "Background: The JSON Web Token specification provides several ways for developers to digitally sign payload claims. This ensures data integrity and robust user authentication. However, some servers fail to properly verify the signature, which can result in them accepting tokens with invalid signatures.", + "Using this vulnerability an attacker can do a full account takeover.

" + + "They can also exploit this vulnerability by supplying an arbitrary claim in the JWT" + + " payload to escalate their privileges or impersonate other users. For example, if the token" + + " contains a \"username\": \"joe\" claim, they could change this to \"username\": \"admin\".", + new String[]{ + "https://redhuntlabs.com/a-practical-guide-to-attack-jwt-json-web-token", + "https://portswigger.net/kb/issues/00200900_jwt-signature-not-verified#:~:text=Description%3A%20JWT%20signature%20not%20verified&text=However%2C%20some%20servers%20fail%20to,privileges%20or%20impersonate%20other%20users." + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + ADD_JKU_TO_JWT( + "ADD_JKU_TO_JWT", + TestCategory.NO_AUTH, + "JWT authentication bypass via jku header injection", + "Since Host server is using the JKU field of the JWT without validating, attacker can tamper with the payload of JWT and access protected resources.", + "The endpoint appears to be vulnerable to broken authentication attack.The original request was replayed by adding the JKU parameter value to the header of" + + " JWT and signing with Akto's key. The server responded with 2XX success codes. This indicates that this endpoint can be accessed with a tampered JWT.

" + + "Background: The JSON Web Signature specification defines the optional \"jku\" header, which contains a URL pointing to a set of keys used by the server to digitally sign " + + "the JWT. This parameter is particularly useful for servers that are configured to use multiple different keys because it can help to determine which key to use when verifying the" + + " signature. If the target application implicitly trusts this header, it may verify the signature using an arbitrary public key obtained from the provided URL, essentially relying " + + "on data that can be tampered with client-side. A malicious user could insert or modify a \"jku\" header so that it points to an external server containing a JSON Web Key Set that " + + "they've generated themselves. They could then re-sign the token using the matching private key and check whether the server still accepts it.", + "Using this vulnerability an attacker can do a full account takeover.

" + + "They can also exploit this vulnerability by supplying an arbitrary claim in the JWT payload to escalate their privileges or impersonate other users. For example, " + + "if the token contains a \"username\": \"joe\" claim, they could change this to \"username\": \"admin\".", + new String[]{ + "https://redhuntlabs.com/a-practical-guide-to-attack-jwt-json-web-token", + "https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-jku-header-injection" + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }), + + CUSTOM_IAM( + "CUSTOM_IAM", + TestCategory.IAM, + "Assets found on the page", + "Fuzzing", + "Fuzzing", + "Fuzzing", + new String[]{ + "fuzzing" + }, new IssueTags[]{ + IssueTags.BL, + IssueTags.OWASPTOP10, + IssueTags.HACKERONETOP10, + }); + + + private final String name; + private final TestCategory superCategory; + private final String issueDescription; + private final String issueDetails; + private final String issueImpact; + private final String testName; + private final String[] references; + private final IssueTags[] issueTags; + private static final TestSubCategory[] valuesArray = values(); + + TestSubCategory(String name, TestCategory superCategory,String testName, String issueDescription, String issueDetails, String issueImpact, String[] references, IssueTags[] issueTags) { + this.name = name; + this.superCategory = superCategory; + this.testName = testName; + this.issueDescription = issueDescription; + this.issueDetails = issueDetails; + this.issueImpact = issueImpact; + this.references = references; + this.issueTags = issueTags; + } + + public static TestSubCategory[] getValuesArray() { + return valuesArray; + } + + public static TestSubCategory getTestCategory(String category) { + for (TestSubCategory testSubCategory : valuesArray) { + if (testSubCategory.name.equalsIgnoreCase(category)) { + return testSubCategory; + } + } + return null; + } + + public String getName() { + return name; + } + + public TestCategory getSuperCategory() { + return superCategory; + } + + public String getIssueDescription() { + return issueDescription; + } + + public String getIssueDetails() { + return issueDetails; + } + + public String getIssueImpact() { + return issueImpact; + } + + public String getTestName() { + return testName; + } + + public String[] getReferences() { + return references; + } + + public IssueTags[] getIssueTags() { + return issueTags; + } + } + + public enum Severity { + CRITICAL, + HIGH, + MEDIUM, + LOW, + INFO + } + + public enum TestRunIssueStatus { + OPEN, + IGNORED, + FIXED + } + + + /* ********************************************************************** */ +} diff --git a/libs/dao/src/main/java/com/akto/util/enums/LoginFlowEnums.java b/libs/dao/src/main/java/com/akto/util/enums/LoginFlowEnums.java new file mode 100644 index 0000000000..b9c323df21 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/enums/LoginFlowEnums.java @@ -0,0 +1,17 @@ +package com.akto.util.enums; + +public class LoginFlowEnums { + + public enum AuthMechanismTypes { + HARDCODED, + LOGIN_REQUEST + } + + public enum LoginStepTypesEnums { + LOGIN_FORM, + MOBILE_CODE_VERIFICATION, + EMAIL_CODE_VERIFICATION, + OTP_VERIFICATION, + RECORDED_FLOW + } +} diff --git a/libs/dao/src/main/java/com/akto/util/enums/MongoDBEnums.java b/libs/dao/src/main/java/com/akto/util/enums/MongoDBEnums.java new file mode 100644 index 0000000000..0f8e556fee --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/enums/MongoDBEnums.java @@ -0,0 +1,30 @@ +package com.akto.util.enums; + +public class MongoDBEnums { + public enum DB { + ACCOUNT + } + + public enum Collection { + + TESTING_RUN_ISSUES ("testing_run_issues", DB.ACCOUNT), + ENDPOINT_LOGICAL_GROUP ("endpoint_logical_group", DB.ACCOUNT), + TEST_ROLES("test_roles", DB.ACCOUNT), + TESTING_RUN_CONFIG("testing_run_config", DB.ACCOUNT); + private final DB db; + private final String collectionName; + + Collection(String name, DB db) { + this.collectionName = name; + this.db = db; + } + + public String getCollectionName() { + return collectionName; + } + + public DB getDb() { + return db; + } + } +} diff --git a/libs/dao/src/main/java/com/akto/util/modifier/AddJkuJWTModifier.java b/libs/dao/src/main/java/com/akto/util/modifier/AddJkuJWTModifier.java new file mode 100644 index 0000000000..051328deab --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/modifier/AddJkuJWTModifier.java @@ -0,0 +1,17 @@ +package com.akto.util.modifier; + +import java.util.HashMap; +import java.util.Map; + +public class AddJkuJWTModifier extends JwtModifier{ + + public static final String JKU_VALUE = "https://raw.githubusercontent.com/akto-api-security/pii-types/master/public_key.pem"; + public static final String JKU_HEADER = "jku"; + + @Override + public String jwtModify(String key, String value) throws Exception { + Map extraHeaders = new HashMap<>(); + extraHeaders.put(JKU_HEADER, JKU_VALUE); + return manipulateJWT(value, extraHeaders, new HashMap<>(),getPrivateKey()); + } +} diff --git a/libs/dao/src/main/java/com/akto/util/modifier/ConvertToArrayPayloadModifier.java b/libs/dao/src/main/java/com/akto/util/modifier/ConvertToArrayPayloadModifier.java new file mode 100644 index 0000000000..3260e9f1d7 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/modifier/ConvertToArrayPayloadModifier.java @@ -0,0 +1,11 @@ +package com.akto.util.modifier; + +import java.util.Collections; + +public class ConvertToArrayPayloadModifier extends PayloadModifier{ + + @Override + public Object modify(String key, Object value) { + return Collections.singletonList(value); + } +} diff --git a/libs/dao/src/main/java/com/akto/util/modifier/InvalidSignatureJWTModifier.java b/libs/dao/src/main/java/com/akto/util/modifier/InvalidSignatureJWTModifier.java new file mode 100644 index 0000000000..c4050fe310 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/modifier/InvalidSignatureJWTModifier.java @@ -0,0 +1,15 @@ +package com.akto.util.modifier; + +public class InvalidSignatureJWTModifier extends JwtModifier { + @Override + public String jwtModify(String key, String value) { + String[] jwtArr = value.split("\\."); + if (jwtArr.length != 3) return null; + + String signature = jwtArr[2]; + String firstChar = signature.split("")[0]; + String finalSignatureFirstChar = firstChar.equals("a") ? "b" : "a" ; + + return jwtArr[0] + "." + jwtArr[1] + "." + finalSignatureFirstChar+signature.substring(1) ; + } +} diff --git a/libs/dao/src/main/java/com/akto/util/modifier/JwtModifier.java b/libs/dao/src/main/java/com/akto/util/modifier/JwtModifier.java new file mode 100644 index 0000000000..3ecd3257aa --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/modifier/JwtModifier.java @@ -0,0 +1,212 @@ +package com.akto.util.modifier; + +import com.akto.dao.context.Context; +import com.akto.dto.type.KeyTypes; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import io.jsonwebtoken.Jwts; + +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.security.KeyFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.*; + +public abstract class JwtModifier extends PayloadModifier { + + static ObjectMapper mapper = new ObjectMapper(); + public abstract String jwtModify(String key, String value) throws Exception; + + @Override + public Object modify(String key, Object value) { + if (value == null) return null; + + String[] splitValue = value.toString().split(" "); + List finalValue = new ArrayList<>(); + boolean flag = false; + + for (String x: splitValue) { + if (KeyTypes.isJWT(x)) { + try { + String modifiedString = jwtModify(key, x); + finalValue.add(modifiedString); + flag = true; + } catch (Exception e) { + ; + return null; + } + } else { + finalValue.add(x); + } + } + + return flag ? String.join( " ", finalValue) : null; + } + + public static String manipulateJWTHeader(String jwt, Map extraHeaders) throws Exception { + Map json = manipulateJWTHeaderToMap(jwt, extraHeaders); + // rebuild string + String modifiedHeaderStr = mapper.writeValueAsString(json); + // encode it and remove trailing = + String encodedModifiedHeader = Base64.getEncoder().encodeToString(modifiedHeaderStr.getBytes(StandardCharsets.UTF_8)); + if (encodedModifiedHeader.endsWith("=")) encodedModifiedHeader = encodedModifiedHeader.substring(0, encodedModifiedHeader.length()-1); + + String[] jwtArr = jwt.split("\\."); + + return encodedModifiedHeader + "." + jwtArr[1]+ "." + jwtArr[2]; + } + + public static Map manipulateJWTHeaderToMap(String jwt, Map extraHeaders) throws Exception { + String[] jwtArr = jwt.split("\\."); + if (jwtArr.length != 3) throw new Exception("Not jwt"); + + String encodedHeader = jwtArr[0]; + byte[] decodedHeaderBytes = Base64.getDecoder().decode(encodedHeader); + String decodedHeaderStr = new String(decodedHeaderBytes, StandardCharsets.UTF_8); + + // convert string to map + Map json = new Gson().fromJson(decodedHeaderStr, Map.class); + // alg -> none + for (String key: extraHeaders.keySet()) { + json.put(key, extraHeaders.get(key)); + } + + return json; + } + + public static String manipulateJWT(String jwt, Map extraHeaders, Map extraBody, String privateKeyString) throws Exception { + + // add header + Map manipulatedJwtHeader = manipulateJWTHeaderToMap(jwt, extraHeaders); + + // add payload + Map bodyJson = extractBodyFromJWT(jwt); + if (extraBody != null) { + for (String key: extraBody.keySet()) { + bodyJson.put(key, extraBody.get(key)); + } + } + + String issueTimeKey = "iat"; + String expiryTimeKey = "exp"; + try { + long issueTime = Double.valueOf((double) bodyJson.get(issueTimeKey)).longValue(); + int newIssueTime = Context.now(); + bodyJson.put(issueTimeKey, newIssueTime+""); + try { + long expiryTime = Double.valueOf((double) bodyJson.get(expiryTimeKey)).longValue(); + long diff = expiryTime - issueTime; + bodyJson.put(expiryTimeKey, (newIssueTime+diff)+""); + } catch (Exception ignored) { + } + } catch (Exception ignored) { + } + + byte [] decoded = Base64.getDecoder().decode(privateKeyString); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded); + KeyFactory kf = KeyFactory.getInstance("RSA"); + + Key privateKey = kf.generatePrivate(keySpec); + + return Jwts.builder() + .setHeader(manipulatedJwtHeader) + .setPayload(mapper.writeValueAsString(bodyJson)) + .signWith(privateKey) + .compact(); + + } + + public static Map extractBodyFromJWT(String jwt) throws Exception { + String[] jwtArr = jwt.split("\\."); + if (jwtArr.length != 3) throw new Exception("Not jwt"); + + Base64.Decoder decoder = Base64.getUrlDecoder(); + + String body = new String(decoder.decode(jwtArr[1])); + + return new Gson().fromJson(body, Map.class); + } + + public static String createJWT(Map claims,String issuer, String subject, int expiryUnit, + int expiryDuration, Map headers) throws Exception { + + + Calendar calendar = Calendar.getInstance(); + java.util.Date issueTime = calendar.getTime(); + + calendar.setTime(issueTime); + calendar.add(expiryUnit, expiryDuration); + java.util.Date expiryTime = calendar.getTime(); + + String privateKeyString = ""; + byte [] decoded = Base64.getDecoder().decode(privateKeyString); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded); + KeyFactory kf = KeyFactory.getInstance("RSA"); + + Key privateKey = kf.generatePrivate(keySpec); + + return Jwts.builder() + .setHeader(headers) + .setIssuer(issuer) + .setSubject(subject) + .addClaims(claims) + .setIssuedAt(issueTime) + .setExpiration(expiryTime) + .signWith(privateKey) + .compact(); + } + + public String getPublicKey() { + String key = "-----BEGIN PUBLIC KEY-----\n" + + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3mbAcFAQKj0OjHof+mhi\n" + + "Rfhpyk4Rs96kiQK1mxYCrNxR5Xi/TeJj1oFEv3EJBI5rJyFz483e0shUl641fr7f\n" + + "XMyEp1Lt2EdGvt9TiwDxKgOMOJOZKcBdbesr1E3egHA2CGt13AZ9a9rEOE4jFZd5\n" + + "jaHhDPFRu4ZioqL18VfLeHP5f955wb5JtGYioawrBfyj7ZuJEJzSpBsBluImnfbi\n" + + "krGocEa1VqPJTnpjNFL2CU6kyzjjg2Zsq1kPJ6d8YA//2DhDUfctahSKN00gI/fe\n" + + "3BwyxwUMXJ9L4QYusuLiXBoZaXLkkWmLa09Fq+OuNiuSjb2wdKhMDdZRisKjvX8+\n" + + "5QIDAQAB\n" + + "-----END PUBLIC KEY-----"; + + return key.replace("-----BEGIN PUBLIC KEY-----","") + .replace("-----END PUBLIC KEY-----","") + .replace("\n",""); + + } + + public String getPrivateKey() { + String key = "-----BEGIN PRIVATE KEY-----\n" + + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDeZsBwUBAqPQ6M\n" + + "eh/6aGJF+GnKThGz3qSJArWbFgKs3FHleL9N4mPWgUS/cQkEjmsnIXPjzd7SyFSX\n" + + "rjV+vt9czISnUu3YR0a+31OLAPEqA4w4k5kpwF1t6yvUTd6AcDYIa3XcBn1r2sQ4\n" + + "TiMVl3mNoeEM8VG7hmKiovXxV8t4c/l/3nnBvkm0ZiKhrCsF/KPtm4kQnNKkGwGW\n" + + "4iad9uKSsahwRrVWo8lOemM0UvYJTqTLOOODZmyrWQ8np3xgD//YOENR9y1qFIo3\n" + + "TSAj997cHDLHBQxcn0vhBi6y4uJcGhlpcuSRaYtrT0Wr4642K5KNvbB0qEwN1lGK\n" + + "wqO9fz7lAgMBAAECggEADELD5y0yxvFYxPvSmX55tHvOcT2+khj7HyaMFoGvIhJ/\n" + + "XVQ7z7JkaKX1wUwdAChN1fltJyjpWNt8dmQ/RL6HF9makpLq09qSFuG+/FHP+c36\n" + + "RAA3GGsne3DUmL62PoRxJiOuerPM5E5KNQRxwLX6GInNG2aOZ/UvqOGtB2IcsIYx\n" + + "8dRDp6PeYlAlKnVrGLWv7eDz9YyyL15Wk8SxM1/ahz3NMovz4iXxAsCAd71zzx1P\n" + + "aOOh3NcHUAvMJ5yojUlmNNsUKHwCtSiGYWe07aPtqPWuiOMGtPJ10hCBvZAtU2ZI\n" + + "cZ/wd0XkaljtXFAnhJq5RR6m0rY/aSO8fgEiZb4lYQKBgQDy8Y7PF5SHbyYoli+5\n" + + "4ELC0dUAoklQydAuuvGKLROYZBqUxgPE8TwM9525HPRRR5LjL0Kef6dLivZ8xPeZ\n" + + "Vie+fF7ZCtd1KAoIuKkZXPn7rXoLr+mYk1nVg/5uvVQ6BQBN0fBEtotIPSuGkE9q\n" + + "LuPOxU48WgSjiWhH3EyO2euwSQKBgQDqWpJy6Iyry+iB5wZtvLvjjF9zNaqbbUej\n" + + "l1U6TT5qoICDhVXTsU24dsgHrIaRKRpPlsldbR212Kv2sADc4NCOAY86jWZ9k+7n\n" + + "rEPsUt7CvQc6ZLmPI2Mqy2ZI2Lmaja/gtparh/Ez7eumXLrAicdQKIVH73vCEZAl\n" + + "MRLtN/9RvQKBgGq3WTf277OeS3DAqC5KKIlTivFAWFw4ik48qCU+L8FdF6AKa1Vz\n" + + "ciFwE8Rgx6F8gzVwaR8ei+pPHH8qNmkQw1yVXUSR0psP/3hdRUpy4QyA43+GwmHX\n" + + "ODrmRDl4ySrDT6LfeV91oDEXTatKcNf/yOnnGbrBABCmJzyJtMd7SmChAoGBAKGd\n" + + "zJlKHpjrinDrbdeH7NtEFx9Qx1NgzaLX3oLSelT2UypgbYwMHlk0MUZ5iGPbQLXa\n" + + "ewvfEDo0LoN1ZWLt92W3VZs/oIrB1mQWvNDhZZZO3gk7JWy9Lsp4cxWRwI4BYGVM\n" + + "BiRNH958GaMlF/VoDvgMub2ePm7bxdigOzk1APLRAoGBAOBYqcqHHJ3P/TMCkSC1\n" + + "k+YI7B2iNFdN8sWQXoQM1KY6UDnU2oI/Cjv4XkeKWQ78mVX0abXo/bXUwaF/ORpR\n" + + "sfUo3tgKMVwS14l/ZQ3TDyM6fHwSwLUOJ+OwUn0licfRrOywqPKzah9r3JjBbC5s\n" + + "K3Qks2pR++7M8QojX+IJfa+a\n" + + "-----END PRIVATE KEY-----"; + + + return key.replace("-----BEGIN PRIVATE KEY-----","") + .replace("-----END PRIVATE KEY-----","") + .replace("\n",""); + } + +} diff --git a/libs/dao/src/main/java/com/akto/util/modifier/NestedObjectModifier.java b/libs/dao/src/main/java/com/akto/util/modifier/NestedObjectModifier.java new file mode 100644 index 0000000000..778f078afb --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/modifier/NestedObjectModifier.java @@ -0,0 +1,13 @@ +package com.akto.util.modifier; + +import com.mongodb.BasicDBObject; + +public class NestedObjectModifier extends PayloadModifier { + + @Override + public Object modify(String key, Object value) { + BasicDBObject res = new BasicDBObject(); + res.put(key, value); + return res; + } +} diff --git a/libs/dao/src/main/java/com/akto/util/modifier/NoneAlgoJWTModifier.java b/libs/dao/src/main/java/com/akto/util/modifier/NoneAlgoJWTModifier.java new file mode 100644 index 0000000000..8e762a71e1 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/modifier/NoneAlgoJWTModifier.java @@ -0,0 +1,15 @@ +package com.akto.util.modifier; + +import java.util.HashMap; +import java.util.Map; + +public class NoneAlgoJWTModifier extends JwtModifier{ + @Override + public String jwtModify(String key, String value) throws Exception { + Map headers = new HashMap<>(); + headers.put("alg", "none"); + String modifiedJWT = manipulateJWTHeader(value, headers); + String[] jwtArr = modifiedJWT.split("\\."); + return jwtArr[0] + "." + jwtArr[1] + "."; + } +} diff --git a/libs/dao/src/main/java/com/akto/util/modifier/PayloadModifier.java b/libs/dao/src/main/java/com/akto/util/modifier/PayloadModifier.java new file mode 100644 index 0000000000..6d9499ffd8 --- /dev/null +++ b/libs/dao/src/main/java/com/akto/util/modifier/PayloadModifier.java @@ -0,0 +1,8 @@ +package com.akto.util.modifier; + +public abstract class PayloadModifier { + + // modify will return null if condition doesn't satisfy or it fails to modify it + public abstract Object modify(String key, Object value); + +} diff --git a/libs/dao/src/test/java/com/akto/dao/DaoConnect.java b/libs/dao/src/test/java/com/akto/dao/DaoConnect.java new file mode 100644 index 0000000000..29895bf064 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dao/DaoConnect.java @@ -0,0 +1,19 @@ +package com.akto.dao; + +import com.akto.DaoInit; +import com.akto.dao.context.Context; +import com.mongodb.ConnectionString; +import org.junit.BeforeClass; + +public class DaoConnect { + + public final static String mongodbURI = "mongodb://localhost:27017"; + + @BeforeClass + public static void setup() { + ConnectionString connectionString = new ConnectionString(mongodbURI); + DaoInit.init(connectionString); + Context.accountId.set(222222); + + } +} diff --git a/libs/dao/src/test/java/com/akto/dao/TestApiCollectionsDao.java b/libs/dao/src/test/java/com/akto/dao/TestApiCollectionsDao.java new file mode 100644 index 0000000000..4bd669342e --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dao/TestApiCollectionsDao.java @@ -0,0 +1,40 @@ +package com.akto.dao; + +import com.akto.dto.ApiCollection; +import com.akto.utils.MongoBasedTest; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +public class TestApiCollectionsDao extends MongoBasedTest { + + @Test + public void testFetchNonTrafficApiCollectionsIds() { + ApiCollectionsDao.instance.getMCollection().drop(); + + // mirroring collection with host + ApiCollection apiCollection1 = new ApiCollection(1000, "mirroring_with_host", 1000, new HashSet<>(), "akto.io", 123); + // mirroring collections without hosts + ApiCollection apiCollection2 = new ApiCollection(2000, "mirroring_without_host", 2000, new HashSet<>(), null, 456); + // manually created collections with vxlanid == 0 + ApiCollection apiCollection3 = new ApiCollection(3000, "burp1", 3000, new HashSet<>(),null, 0); + // manually created collections with vxlanid != 0 + ApiCollection apiCollection4 = new ApiCollection(4000, "burp2", 4000, new HashSet<>(),null, 4000); + + ApiCollectionsDao.instance.insertMany(Arrays.asList(apiCollection1, apiCollection2, apiCollection3, apiCollection4)); + + List apiCollectionIds = ApiCollectionsDao.instance.fetchNonTrafficApiCollectionsIds(); + + assertEquals(3, apiCollectionIds.size()); + + Set apiCollectionIdsSet = new HashSet<>(apiCollectionIds); + + assertEquals(apiCollectionIdsSet, new HashSet<>(Arrays.asList(2000,3000,4000))); + + } +} diff --git a/libs/dao/src/test/java/com/akto/dao/TestSampleDataDao.java b/libs/dao/src/test/java/com/akto/dao/TestSampleDataDao.java new file mode 100644 index 0000000000..afa7bf196e --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dao/TestSampleDataDao.java @@ -0,0 +1,70 @@ +package com.akto.dao; + +import com.akto.dto.traffic.Key; +import com.akto.dto.traffic.SampleData; +import com.akto.dto.type.URLMethods; +import com.akto.utils.MongoBasedTest; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class TestSampleDataDao extends MongoBasedTest { + + @Test + public void testFetchSampleDataPaginated() { + SampleDataDao.instance.getMCollection().drop(); + + int limit = 10; + + List sampleDataList = new ArrayList<>(); + List urls = Arrays.asList( + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P" + ); + for (String u: urls) { + String url = "/api/" + u; + for (URLMethods.Method method: Arrays.asList(URLMethods.Method.POST, URLMethods.Method.GET)) { + Key key = new Key(123, url, method, 200, 0,0); + SampleData sampleData = new SampleData(key, Collections.singletonList("")); + sampleDataList.add(sampleData); + } + } + + SampleDataDao.instance.insertMany(sampleDataList); + + + sampleDataList = SampleDataDao.instance.fetchSampleDataPaginated( + 123, null, null, limit + ); + + int lastIdx = sampleDataList.size()-1; + + SampleData firstSampleData = sampleDataList.get(0); + SampleData lastSampleData = sampleDataList.get(lastIdx); + + assertEquals("/api/A", firstSampleData.getId().getUrl()); + assertEquals("GET", firstSampleData.getId().getMethod().name()); + assertEquals("/api/E", lastSampleData.getId().getUrl()); + assertEquals("POST", lastSampleData.getId().getMethod().name()); + + // continue + sampleDataList = SampleDataDao.instance.fetchSampleDataPaginated( + 123, lastSampleData.getId().getUrl(), lastSampleData.getId().getMethod().name(), limit + ); + + lastIdx = sampleDataList.size()-1; + + firstSampleData = sampleDataList.get(0); + lastSampleData = sampleDataList.get(lastIdx); + + assertEquals("/api/F", firstSampleData.getId().getUrl()); + assertEquals("GET", firstSampleData.getId().getMethod().name()); + assertEquals("/api/J", lastSampleData.getId().getUrl()); + assertEquals("POST", lastSampleData.getId().getMethod().name()); + + } +} diff --git a/libs/dao/src/test/java/com/akto/dao/TestSingleTypeInfoDao.java b/libs/dao/src/test/java/com/akto/dao/TestSingleTypeInfoDao.java new file mode 100644 index 0000000000..65f7e30648 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dao/TestSingleTypeInfoDao.java @@ -0,0 +1,393 @@ +package com.akto.dao; + +import com.akto.DaoInit; +import com.akto.dao.context.Context; +import com.akto.dto.ApiInfo; +import com.akto.dto.CustomDataType; +import com.akto.dto.IgnoreData; +import com.akto.dto.SensitiveParamInfo; +import com.akto.dto.data_types.Conditions; +import com.akto.dto.data_types.EndsWithPredicate; +import com.akto.dto.data_types.RegexPredicate; +import com.akto.dto.data_types.StartsWithPredicate; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.types.CappedSet; +import com.akto.utils.MongoBasedTest; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.*; +import org.bson.conversions.Bson; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; + +public class TestSingleTypeInfoDao extends MongoBasedTest { + + // testDefaultDomain to test if single type info in db has domain value null then if default gets set to ENUM or not + @Test + public void testDefaultDomain() { + SingleTypeInfoDao.instance.getMCollection().drop(); + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + "url", "GET",200, false, "param#key", SingleTypeInfo.EMAIL, 0, false + ); + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 0,0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + SingleTypeInfoDao.instance.updateOne( + SingleTypeInfoDao.createFilters(singleTypeInfo), Updates.inc("count",1) + ); + + SingleTypeInfo singleTypeInfoFromDb = SingleTypeInfoDao.instance.findOne(new BasicDBObject()); + assertEquals(SingleTypeInfo.Domain.ENUM, singleTypeInfoFromDb.getDomain()); + + } + + @Test + public void testInsertAndFetchAktoDefined() { + SingleTypeInfoDao.instance.getMCollection().drop(); + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + "url", "GET",200, false, "param#key", SingleTypeInfo.EMAIL, 0, false + ); + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 0,0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + SingleTypeInfoDao.instance.updateOne( + SingleTypeInfoDao.createFilters(singleTypeInfo), Updates.inc("count",1) + ); + + SingleTypeInfo singleTypeInfoFromDb = SingleTypeInfoDao.instance.findOne(new BasicDBObject()); + + assertEquals(singleTypeInfoFromDb.getSubType(), SingleTypeInfo.EMAIL); + } + + @Test + public void testInsertAndFetchCustom() { + SingleTypeInfoDao.instance.getMCollection().drop(); + CustomDataTypeDao.instance.getMCollection().drop(); + Context.accountId.set(1_000_000); + IgnoreData ignoreData = new IgnoreData(new HashMap<>(), new HashSet<>()); + Conditions keyConditions = new Conditions(Arrays.asList(new StartsWithPredicate("we"), new RegexPredicate("reg")), Conditions.Operator.AND); + Conditions valueConditions = new Conditions(Collections.singletonList(new EndsWithPredicate("something")), Conditions.Operator.OR); + CustomDataType customDataType = new CustomDataType( + "custom1", false, Arrays.asList(SingleTypeInfo.Position.REQUEST_PAYLOAD, SingleTypeInfo.Position.RESPONSE_PAYLOAD), + 0,true, keyConditions, valueConditions, Conditions.Operator.OR,ignoreData + ); + + CustomDataTypeDao.instance.insertOne(customDataType); + SingleTypeInfo.fetchCustomDataTypes(); + + assertEquals(SingleTypeInfo.customDataTypeMap.keySet().size(), 1); + + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + "url", "GET",200, false, "param#key", customDataType.toSubType(), 0, false + ); + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 0,0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + SingleTypeInfoDao.instance.updateOne( + SingleTypeInfoDao.createFilters(singleTypeInfo), Updates.inc("count",1) + ); + + + SingleTypeInfo singleTypeInfoFromDb = SingleTypeInfoDao.instance.findOne(SingleTypeInfoDao.createFilters(singleTypeInfo)); + + assertEquals(singleTypeInfoFromDb.getParam(), "param#key"); + assertEquals(singleTypeInfoFromDb.getSubType(), customDataType.toSubType()); + + } + + + private WriteModel createSingleTypeInfoUpdate(String url, String method, SingleTypeInfo.SubType subType, int apiCollectionId, int responseCode) { + SingleTypeInfoDao.instance.getMCollection().drop(); + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + url, method,responseCode, false, "param", subType, apiCollectionId, false + ); + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 100,0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + UpdateOptions updateOptions = new UpdateOptions(); + updateOptions.upsert(true); + return new UpdateOneModel<>(SingleTypeInfoDao.createFilters(singleTypeInfo), Updates.combine(Updates.set("count",1),Updates.set("timestamp",singleTypeInfo.getTimestamp())),updateOptions); + } + + @Test + public void testFilterForSensitiveParamsExcludingUserMarkedSensitive() { + SingleTypeInfoDao.instance.getMCollection().drop(); + CustomDataTypeDao.instance.getMCollection().drop(); + Context.accountId.set(1_000_000); + List> bulkWrites = new ArrayList<>(); + bulkWrites.add(createSingleTypeInfoUpdate("A", "GET", SingleTypeInfo.EMAIL, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("B", "POST", SingleTypeInfo.INTEGER_32, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("C", "GET", SingleTypeInfo.JWT, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("D", "POST", SingleTypeInfo.JWT, 0,-1)); + IgnoreData ignoreData = new IgnoreData(new HashMap<>(), new HashSet<>()); + CustomDataType customDataType1 = new CustomDataType("CUSTOM_DATA_1", true, Collections.emptyList(), 0,true, null,null, Conditions.Operator.AND,ignoreData); + CustomDataType customDataType2 = new CustomDataType("CUSTOM_DATA_2",false, Collections.emptyList(), 0,true, null,null, Conditions.Operator.AND,ignoreData); + CustomDataTypeDao.instance.insertMany(Arrays.asList(customDataType1, customDataType2)); + + bulkWrites.add(createSingleTypeInfoUpdate("E", "POST",customDataType1.toSubType(), 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("F", "POST",customDataType2.toSubType(), 0,200)); + + bulkWrites.add(createSingleTypeInfoUpdate("G", "POST", SingleTypeInfo.EMAIL, 0,-1)); + bulkWrites.add(createSingleTypeInfoUpdate("G", "GET", SingleTypeInfo.EMAIL, 0,-1)); + bulkWrites.add(createSingleTypeInfoUpdate("H", "GET", SingleTypeInfo.EMAIL, 1,-1)); + + SingleTypeInfo.fetchCustomDataTypes();; + + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkWrites); + + List singleTypeInfoList = SingleTypeInfoDao.instance.findAll(new BasicDBObject()); + assertEquals(singleTypeInfoList.size(),9); + + Bson filter = SingleTypeInfoDao.instance.filterForSensitiveParamsExcludingUserMarkedSensitive(null,null,null); + + List sensitiveSingleTypeInfos = SingleTypeInfoDao.instance.findAll(filter); + assertEquals(sensitiveSingleTypeInfos.size(), 6); + + + Set sensitiveEndpoints = SingleTypeInfoDao.instance.getSensitiveEndpoints(0, null, null); + assertEquals(sensitiveEndpoints.size(), 4); + + SensitiveParamInfoDao.instance.insertOne( + new SensitiveParamInfo("I", "GET", 200, false, "param", 0, true) + ); + + sensitiveEndpoints = SingleTypeInfoDao.instance.getSensitiveEndpoints(0, null, null); + assertEquals(sensitiveEndpoints.size(), 5); + } + + @Test + public void testFilterForAllNewParams(){ + SingleTypeInfoDao.instance.getMCollection().drop(); + + List> bulkWrites = new ArrayList<>(); + bulkWrites.add(createSingleTypeInfoUpdate("A", "GET", SingleTypeInfo.EMAIL, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("B", "GET", SingleTypeInfo.EMAIL, 0,200)); + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkWrites); + + Bson filterNewParams = SingleTypeInfoDao.instance.filterForAllNewParams(0,0); + + List list = SingleTypeInfoDao.instance.findAll(filterNewParams); + assertEquals(list.size(),2); + } + + @Test + public void testResetCount() { + SingleTypeInfoDao.instance.getMCollection().drop(); + + List> bulkWrites = new ArrayList<>(); + bulkWrites.add(createSingleTypeInfoUpdate("A", "GET", SingleTypeInfo.EMAIL, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("B", "GET", SingleTypeInfo.EMAIL, 0,200)); + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkWrites); + + SingleTypeInfoDao.instance.resetCount(); + + List singleTypeInfoList = SingleTypeInfoDao.instance.findAll(new BasicDBObject()); + + for (SingleTypeInfo singleTypeInfo: singleTypeInfoList) { + if (singleTypeInfo.getCount() != 0) { + fail(); + } + } + + } + + @Test + public void testFetchEndpointsInCollection() { + SingleTypeInfoDao.instance.getMCollection().drop(); + + List> bulkWrites = new ArrayList<>(); + bulkWrites.add(createSingleTypeInfoUpdate("A", "GET", SingleTypeInfo.EMAIL, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("A", "GET", SingleTypeInfo.GENERIC, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("A", "GET", SingleTypeInfo.EMAIL, 0,201)); + bulkWrites.add(createSingleTypeInfoUpdate("A", "POST", SingleTypeInfo.EMAIL, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("B", "POST", SingleTypeInfo.EMAIL, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("B", "GET", SingleTypeInfo.EMAIL, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("A", "GET", SingleTypeInfo.EMAIL, 1,200)); + bulkWrites.add(createSingleTypeInfoUpdate("C", "GET", SingleTypeInfo.EMAIL, 1,200)); + bulkWrites.add(createSingleTypeInfoUpdate("A", "GET", SingleTypeInfo.EMAIL, -1000,200)); + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkWrites); + + List apiInfoKeyList0 = SingleTypeInfoDao.instance.fetchEndpointsInCollection(0); + List apiInfoKeyList1 = SingleTypeInfoDao.instance.fetchEndpointsInCollection(1); + List apiInfoKeyList2 = SingleTypeInfoDao.instance.fetchEndpointsInCollection(-1); + List apiInfoKeyList3 = SingleTypeInfoDao.instance.fetchEndpointsInCollection(-1000); + + assertEquals(apiInfoKeyList0.size(), 4); + assertEquals(apiInfoKeyList1.size(), 2); + assertEquals(apiInfoKeyList2.size(), 7); + assertEquals(apiInfoKeyList3.size(), 1); + + } + + @Test + public void testDeleteValues() { + SingleTypeInfoDao.instance.getMCollection().drop(); + + List> bulkWrites = new ArrayList<>(); + bulkWrites.add(createSingleTypeInfoUpdate("A", "GET", SingleTypeInfo.EMAIL, 0,200)); + bulkWrites.add(createSingleTypeInfoUpdate("A", "GET", SingleTypeInfo.GENERIC, 0,200)); + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkWrites); + + SingleTypeInfoDao.instance.getMCollection().updateMany( + new BasicDBObject(), + Updates.addEachToSet(SingleTypeInfo._VALUES +".elements",Arrays.asList("a","b","c")) + ); + + List singleTypeInfoList = SingleTypeInfoDao.instance.fetchAll(); + assertEquals(2, singleTypeInfoList.size()); + for (SingleTypeInfo singleTypeInfo: singleTypeInfoList) { + assertEquals(3, singleTypeInfo.getValues().getElements().size()); + } + + SingleTypeInfoDao.instance.deleteValues(); + singleTypeInfoList = SingleTypeInfoDao.instance.fetchAll(); + assertEquals(2, singleTypeInfoList.size()); + for (SingleTypeInfo singleTypeInfo: singleTypeInfoList) { + assertEquals(0, singleTypeInfo.getValues().getElements().size()); + } + } + + @Test + public void testCreateFiltersWithoutSubType() { + SingleTypeInfoDao.instance.getMCollection().drop(); + // insert 2 identical params but without different subType. Then update using createFiltersWithoutSubType + + SingleTypeInfo.ParamId paramId1 = new SingleTypeInfo.ParamId("url", "GET", 200, false, "param1", SingleTypeInfo.GENERIC, 0, false); + SingleTypeInfo sti1 = new SingleTypeInfo(paramId1, new HashSet<>(), new HashSet<>(), 0, 0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + + SingleTypeInfo.ParamId paramId2 = new SingleTypeInfo.ParamId("url", "GET", 200, false, "param1", SingleTypeInfo.EMAIL, 0, false); + SingleTypeInfo sti2 = new SingleTypeInfo(paramId2, new HashSet<>(), new HashSet<>(), 0, 0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + + SingleTypeInfo.ParamId paramId3 = new SingleTypeInfo.ParamId("url_next", "GET", 200, false, "param1", SingleTypeInfo.EMAIL, 0, false); + SingleTypeInfo sti3 = new SingleTypeInfo(paramId3, new HashSet<>(), new HashSet<>(), 0, 0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + + SingleTypeInfo.ParamId paramId4 = new SingleTypeInfo.ParamId("url", "POST", 200, false, "param1", SingleTypeInfo.EMAIL, 0, false); + SingleTypeInfo sti4 = new SingleTypeInfo(paramId4, new HashSet<>(), new HashSet<>(), 0, 0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + + SingleTypeInfo.ParamId paramId5 = new SingleTypeInfo.ParamId("url", "GET",200,true, "param1", SingleTypeInfo.EMAIL, 0, false); + SingleTypeInfo sti5 = new SingleTypeInfo(paramId5, new HashSet<>(), new HashSet<>(), 0, 0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + + SingleTypeInfo.ParamId paramId6 = new SingleTypeInfo.ParamId("url", "GET",200,false, "param1", SingleTypeInfo.EMAIL, 0,true); + SingleTypeInfo sti6 = new SingleTypeInfo(paramId6, new HashSet<>(), new HashSet<>(), 0, 0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + + + List> bulkWrites = new ArrayList<>(); + bulkWrites.add(new UpdateOneModel<>(SingleTypeInfoDao.createFilters(sti1), Updates.set("count",1), new UpdateOptions().upsert(true))); + bulkWrites.add(new UpdateOneModel<>(SingleTypeInfoDao.createFilters(sti2), Updates.set("count",1), new UpdateOptions().upsert(true))); + bulkWrites.add(new UpdateOneModel<>(SingleTypeInfoDao.createFilters(sti3), Updates.set("count",1), new UpdateOptions().upsert(true))); + bulkWrites.add(new UpdateOneModel<>(SingleTypeInfoDao.createFilters(sti4), Updates.set("count",1), new UpdateOptions().upsert(true))); + bulkWrites.add(new UpdateOneModel<>(SingleTypeInfoDao.createFilters(sti5), Updates.set("count",1), new UpdateOptions().upsert(true))); + bulkWrites.add(new UpdateOneModel<>(SingleTypeInfoDao.createFilters(sti6), Updates.set("count",1), new UpdateOptions().upsert(true))); + + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkWrites); + + List> bulkWritesMany = new ArrayList<>(); + // only 1 update will update both params since only subType is different + // but won't update STIs other than sti1 and sti2 + bulkWritesMany.add(new UpdateManyModel<>(SingleTypeInfoDao.createFiltersWithoutSubType(sti1), Updates.set("count",100), new UpdateOptions().upsert(false))); + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkWritesMany); + + int count = 0; + List singleTypeInfos = SingleTypeInfoDao.instance.findAll(new BasicDBObject()); + for (SingleTypeInfo singleTypeInfo: singleTypeInfos) { + if (singleTypeInfo.equals(sti1) || singleTypeInfo.equals(sti2)) { + count ++ ; + assertEquals(100,singleTypeInfo.getCount()); + } else { + assertEquals(1, singleTypeInfo.getCount()); + } + } + + assertEquals(bulkWrites.size(), singleTypeInfos.size()); + assertEquals(2, count); + } + + // Test to see what happens when singleTypeInfo + @Test + public void testAddingIsUrlParam() { + SingleTypeInfoDao.instance.getMCollection().drop(); + + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + "url", "GET",200, false, "param#key", SingleTypeInfo.EMAIL, 0, false + ); + SingleTypeInfo info = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 0,0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + + List filters = new ArrayList<>(); + filters.add(Filters.eq("url", info.getUrl())); + filters.add(Filters.eq("method", info.getMethod())); + filters.add(Filters.eq("responseCode", info.getResponseCode())); + filters.add(Filters.eq("isHeader", info.getIsHeader())); + filters.add(Filters.eq("param", info.getParam())); + filters.add(Filters.eq("apiCollectionId", info.getApiCollectionId())); + filters.add(Filters.eq("subType", info.getSubType().getName())); + + SingleTypeInfoDao.instance.updateOne(Filters.and(filters), Updates.set("count",1)); + long count = SingleTypeInfoDao.instance.getMCollection().countDocuments(); + assertEquals(1, count); + // to make sure isUrlParam doesn't exist + SingleTypeInfo stiFromDb = SingleTypeInfoDao.instance.findOne(Filters.exists(SingleTypeInfo._IS_URL_PARAM)); + assertNull(stiFromDb); + + SingleTypeInfoDao.instance.updateOne(SingleTypeInfoDao.createFilters(info), Updates.set("count",1)); + count = SingleTypeInfoDao.instance.getMCollection().countDocuments(); + assertEquals(1, count); + + info.setIsUrlParam(true); + SingleTypeInfoDao.instance.updateOne(SingleTypeInfoDao.createFilters(info), Updates.set("count",1)); + count = SingleTypeInfoDao.instance.getMCollection().countDocuments(); + assertEquals(2, count); + } + + @Test + public void testInsert() { + SingleTypeInfoDao.instance.getMCollection().drop(); + + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + "url", "GET",200, false, "param#key", SingleTypeInfo.EMAIL, 0, false + ); + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 100,1000,30, new CappedSet<>(), SingleTypeInfo.Domain.RANGE, -1000, 10000); + singleTypeInfo.setUniqueCount(1000); + singleTypeInfo.setPublicCount(100); + + SingleTypeInfoDao.instance.insertOne(singleTypeInfo); + + SingleTypeInfo singleTypeInfoFromDb = SingleTypeInfoDao.instance.findOne(new BasicDBObject()); + assertEquals(singleTypeInfo, singleTypeInfoFromDb); + + assertEquals(singleTypeInfo.getMaxValue(), singleTypeInfoFromDb.getMaxValue()); + assertEquals(singleTypeInfo.getMinValue(), singleTypeInfoFromDb.getMinValue()); + assertEquals(singleTypeInfo.getUniqueCount(), singleTypeInfoFromDb.getUniqueCount()); + assertEquals(singleTypeInfo.getPublicCount(), singleTypeInfoFromDb.getPublicCount()); + assertEquals(singleTypeInfo.getCount(), singleTypeInfoFromDb.getCount()); + assertEquals(singleTypeInfo.getValues().count(), singleTypeInfoFromDb.getValues().count()); + } + + @Test + public void testFetchStiOfCollections() { + SingleTypeInfoDao.instance.getMCollection().drop(); + + SingleTypeInfo sti1 = generateSTIUsingCollectionId(1000, "url1", "param1"); + SingleTypeInfo sti2 = generateSTIUsingCollectionId(1000, "url1", "param2"); + SingleTypeInfo sti3 = generateSTIUsingCollectionId(2000, "url2", "param3"); + SingleTypeInfo sti4 = generateSTIUsingCollectionId(2000, "url3", "param4"); + SingleTypeInfo sti5 = generateSTIUsingCollectionId(3000, "url4", "param4"); + + SingleTypeInfoDao.instance.insertMany(Arrays.asList(sti1, sti2, sti3, sti4, sti5)); + + List singleTypeInfos = SingleTypeInfoDao.instance.fetchStiOfCollections(Arrays.asList(1000,3000)); + + assertEquals(3, singleTypeInfos.size()); + Map countMap = new HashMap<>(); + for (SingleTypeInfo singleTypeInfo: singleTypeInfos) { + int c = countMap.getOrDefault(singleTypeInfo.getApiCollectionId(), 0); + countMap.put(singleTypeInfo.getApiCollectionId(), c+1); + } + + assertEquals(2,(int) countMap.get(1000)); + assertNull(countMap.get(2000)); + assertEquals(1,(int) countMap.get(3000)); + } + + + private SingleTypeInfo generateSTIUsingCollectionId(int apiCollectionId, String url, String param) { + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + url, "GET",200, false, param, SingleTypeInfo.GENERIC, apiCollectionId, false + ); + return new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 100,1000,30, new CappedSet<>(), SingleTypeInfo.Domain.RANGE, -1000, 10000); + } +} diff --git a/libs/dao/src/test/java/com/akto/dao/TestUsersDao.java b/libs/dao/src/test/java/com/akto/dao/TestUsersDao.java new file mode 100644 index 0000000000..5d88cd276a --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dao/TestUsersDao.java @@ -0,0 +1,26 @@ +package com.akto.dao; + +import com.akto.dao.context.Context; +import com.akto.dto.User; +import com.akto.utils.MongoBasedTest; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TestUsersDao extends MongoBasedTest { + + @Test + public void testGetFirstUser() { + UsersDao.instance.getMCollection().drop(); + UsersDao.instance.insertOne(new User("avneesh", "avneesh@akto.io", null,null)); + + User firstUser = UsersDao.instance.getFirstUser(); + assertEquals(firstUser.getLogin(), "avneesh@akto.io"); + + User secondUser = new User("ankush", "ankush@akto.io", null,null); + secondUser.setId(Context.now()+1000); + UsersDao.instance.insertOne(secondUser); + firstUser = UsersDao.instance.getFirstUser(); + assertEquals(firstUser.getLogin(), "avneesh@akto.io"); + } +} diff --git a/libs/dao/src/test/java/com/akto/dao/VCDemoData.java b/libs/dao/src/test/java/com/akto/dao/VCDemoData.java new file mode 100644 index 0000000000..d3a3618582 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dao/VCDemoData.java @@ -0,0 +1,91 @@ +package com.akto.dao; + +import com.akto.DaoInit; +import com.akto.dao.context.Context; +import com.akto.dto.ApiCollection; +import com.akto.dto.ApiInfo; +import com.akto.dto.type.SingleTypeInfo; +import com.akto.dto.type.URLMethods; +import com.akto.types.CappedSet; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.UpdateOneModel; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.Updates; +import com.mongodb.client.model.WriteModel; +import org.bson.conversions.Bson; + +import java.util.*; + +public class VCDemoData { + public static void main(String[] args) { + DaoInit.init(new ConnectionString("mongodb://52.91.42.30:27017/admini")); + Context.accountId.set(1_000_000); + RuntimeFilterDao.instance.initialiseFilters(); + +// ApiCollectionsDao.instance.insertOne(new ApiCollection(0, "Default", Context.now(), new HashSet<>(), null,0)); + Set urls1 = new HashSet<>(); + for (int i=0; i<8; i++) urls1.add(i+""); + ApiCollectionsDao.instance.insertOne(new ApiCollection(1, null, Context.now(), urls1,"api.akto.io",0)); + Set urls2 = new HashSet<>(); + for (int i=0; i<10; i++) urls2.add(i+""); + ApiCollectionsDao.instance.insertOne(new ApiCollection(2, null, Context.now(), urls2,"staging.akto.io",0)); + Set urls3 = new HashSet<>(); + for (int i=0; i<9; i++) urls3.add(i+""); + ApiCollectionsDao.instance.insertOne(new ApiCollection(3, null, Context.now(), urls3,"prod.akto.io",0)); + + + List singleTypeInfoList = new ArrayList<>(); + List apiInfos = new ArrayList<>(); + int acid= 1; + + fill(singleTypeInfoList, apiInfos, "/api/users", SingleTypeInfo.EMAIL, acid, 23); + fill(singleTypeInfoList, apiInfos, "/api/players", SingleTypeInfo.EMAIL, acid, 29); + fill(singleTypeInfoList, apiInfos, "/api/users/payment", SingleTypeInfo.CREDIT_CARD, acid, 17); + fill(singleTypeInfoList, apiInfos, "/api/users/contact", SingleTypeInfo.PHONE_NUMBER, acid,19); + fill(singleTypeInfoList, apiInfos, "/api/games", SingleTypeInfo.GENERIC, acid, 25); + fill(singleTypeInfoList, apiInfos, "/api/dashboard", SingleTypeInfo.GENERIC, acid, 29); + fill(singleTypeInfoList, apiInfos, "/api/config", SingleTypeInfo.GENERIC, acid, 21); + fill(singleTypeInfoList, apiInfos, "/api/dashboard/INTEGER", SingleTypeInfo.GENERIC, acid,38); + fill(singleTypeInfoList, apiInfos, "/api/cron", SingleTypeInfo.GENERIC, acid,33); + fill(singleTypeInfoList, apiInfos, "/api/boards", SingleTypeInfo.GENERIC, acid,33); + fill(singleTypeInfoList, apiInfos, "/api/inventory", SingleTypeInfo.GENERIC, acid,33); + fill(singleTypeInfoList, apiInfos, "/api/login", SingleTypeInfo.EMAIL, acid,33); + fill(singleTypeInfoList, apiInfos, "/api/settings", SingleTypeInfo.GENERIC, acid,33); + fill(singleTypeInfoList, apiInfos, "/api/events", SingleTypeInfo.GENERIC, acid,33); + + + ArrayList> bulkUpdates = new ArrayList<>(); + for (SingleTypeInfo singleTypeInfo: singleTypeInfoList) { + Bson filters = SingleTypeInfoDao.createFilters(singleTypeInfo); + bulkUpdates.add(new UpdateOneModel<>(filters, Updates.set("timestamp", Context.now()), new UpdateOptions().upsert(true))); + } + + SingleTypeInfoDao.instance.getMCollection().bulkWrite(bulkUpdates); + ApiInfoDao.instance.insertMany(apiInfos); + + + } + + public static void fill(List singleTypeInfoList, List apiInfos, String url, SingleTypeInfo.SubType subType, int acid, int r) { + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId(url, "GET", 200, false, "", subType, acid, false); + SingleTypeInfo sit = new SingleTypeInfo(paramId, null,null, 0,Context.now(),0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + + singleTypeInfoList.add(sit); + + for (int i=0;i < r; i++) { + SingleTypeInfo.ParamId paramId1 = new SingleTypeInfo.ParamId(url, "GET", 200, false, i+"", SingleTypeInfo.GENERIC, acid, false); + SingleTypeInfo sit1 = new SingleTypeInfo(paramId1, null,null, 0,Context.now(),0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + singleTypeInfoList.add(sit1); + } + + ApiInfo apiInfo = new ApiInfo(acid, url, URLMethods.Method.GET); + apiInfo.setApiAccessTypes(new HashSet<>(Collections.singleton(ApiInfo.ApiAccessType.PUBLIC))); + Set s = new HashSet<>(); + s.add(ApiInfo.AuthType.API_TOKEN); + Set> g = new HashSet<>(); + g.add(s); + apiInfo.setAllAuthTypesFound(g); + apiInfos.add(apiInfo); + } +} diff --git a/libs/dao/src/test/java/com/akto/dto/TestApiInfo.java b/libs/dao/src/test/java/com/akto/dto/TestApiInfo.java new file mode 100644 index 0000000000..babdc90547 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/TestApiInfo.java @@ -0,0 +1,77 @@ +package com.akto.dto; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TestApiInfo { + + @Test + public void testCalculateActualAuth1() { + ApiInfo apiInfo = new ApiInfo(); + apiInfo.setAllAuthTypesFound(new HashSet<>()); + + Set a = new HashSet<>(); + a.add(ApiInfo.AuthType.JWT); + apiInfo.getAllAuthTypesFound().add(a); + + + Set b = new HashSet<>(); + b.add(ApiInfo.AuthType.BEARER); + b.add(ApiInfo.AuthType.UNAUTHENTICATED); + apiInfo.getAllAuthTypesFound().add(b); + + apiInfo.calculateActualAuth(); + + assertEquals(apiInfo.getActualAuthType(), Collections.singletonList(ApiInfo.AuthType.UNAUTHENTICATED)); + } + + @Test + public void testCalculateActualAuth2() { + ApiInfo apiInfo = new ApiInfo(); + apiInfo.setAllAuthTypesFound(new HashSet<>()); + + Set a = new HashSet<>(); + a.add(ApiInfo.AuthType.JWT); + a.add(ApiInfo.AuthType.BEARER); + apiInfo.getAllAuthTypesFound().add(a); + + + Set b = new HashSet<>(); + b.add(ApiInfo.AuthType.BEARER); + apiInfo.getAllAuthTypesFound().add(b); + + apiInfo.calculateActualAuth(); + + assertEquals(apiInfo.getActualAuthType(), Collections.singletonList(ApiInfo.AuthType.BEARER)); + } + + @Test + public void testCalculateActualAuth3() { + ApiInfo apiInfo = new ApiInfo(); + apiInfo.setAllAuthTypesFound(new HashSet<>()); + + Set a = new HashSet<>(); + a.add(ApiInfo.AuthType.JWT); + a.add(ApiInfo.AuthType.BEARER); + apiInfo.getAllAuthTypesFound().add(a); + + Set b = new HashSet<>(); + b.add(ApiInfo.AuthType.JWT); + apiInfo.getAllAuthTypesFound().add(b); + + Set c = new HashSet<>(); + c.add(ApiInfo.AuthType.BASIC); + apiInfo.getAllAuthTypesFound().add(c); + + apiInfo.calculateActualAuth(); + + assertTrue(apiInfo.getActualAuthType().containsAll(Arrays.asList(ApiInfo.AuthType.BASIC, ApiInfo.AuthType.JWT))); + } +} diff --git a/libs/dao/src/test/java/com/akto/dto/TestOriginalHttpRequest.java b/libs/dao/src/test/java/com/akto/dto/TestOriginalHttpRequest.java new file mode 100644 index 0000000000..93a36f6a09 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/TestOriginalHttpRequest.java @@ -0,0 +1,128 @@ +package com.akto.dto; + +import com.akto.dto.type.RequestTemplate; +import com.mongodb.BasicDBObject; +import org.junit.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +public class TestOriginalHttpRequest { + + private OriginalHttpRequest generateOriginalHttpRequest() { + OriginalHttpRequest originalHttpRequest = new OriginalHttpRequest(); + String message = "{\"method\":\"POST\",\"requestPayload\":\"[\\n {\\n \\\"id\\\": 0,\\n \\\"username\\\": \\\"string\\\",\\n \\\"firstName\\\": \\\"string\\\",\\n \\\"lastName\\\": \\\"string\\\",\\n \\\"email\\\": \\\"string\\\",\\n \\\"password\\\": \\\"string\\\",\\n \\\"phone\\\": \\\"string\\\",\\n \\\"userStatus\\\": 0\\n }\\n]\",\"responsePayload\":\"{\\\"code\\\":200,\\\"type\\\":\\\"unknown\\\",\\\"message\\\":\\\"ok\\\"}\",\"ip\":\"null\",\"source\":\"HAR\",\"type\":\"HTTP/2\",\"akto_vxlan_id\":\"1661807253\",\"path\":\"https://petstore.swagger.io/v2/user/createWithArray?user=1\",\"requestHeaders\":\"{\\\"Origin\\\":\\\"https://petstore.swagger.io\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"TE\\\":\\\"trailers\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Content-Length\\\":\\\"195\\\",\\\"Content-Type\\\":\\\"application/json\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:14:27 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\",\"time\":\"1641327267\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + originalHttpRequest.buildFromSampleMessage(message); + return originalHttpRequest; + } + + @Test + public void testBuildFromSampleMessage() { + OriginalHttpRequest originalHttpRequest = generateOriginalHttpRequest(); + assertEquals("https://petstore.swagger.io/v2/user/createWithArray", originalHttpRequest.getUrl()); + assertEquals("HTTP/2", originalHttpRequest.getType()); + assertEquals("user=1", originalHttpRequest.getQueryParams()); + assertEquals("POST", originalHttpRequest.getMethod()); + assertEquals("[\n {\n \"id\": 0,\n \"username\": \"string\",\n \"firstName\": \"string\",\n \"lastName\": \"string\",\n \"email\": \"string\",\n \"password\": \"string\",\n \"phone\": \"string\",\n \"userStatus\": 0\n }\n]", originalHttpRequest.getBody()); + assertEquals(14, originalHttpRequest.getHeaders().size()); + assertEquals("petstore.swagger.io", originalHttpRequest.getHeaders().get("host").get(0)); + } + + @Test + public void testHeaderFunctions() { + OriginalHttpRequest originalHttpRequest = generateOriginalHttpRequest(); + assertEquals("petstore.swagger.io", originalHttpRequest.findHostFromHeader()); + assertEquals("application/json", originalHttpRequest.findContentType()); + } + + @Test + public void testGetFullUrlWithParams() { + OriginalHttpRequest originalHttpRequest = generateOriginalHttpRequest(); + String fullUrlWithParams = originalHttpRequest.getFullUrlWithParams(); + assertEquals("https://petstore.swagger.io/v2/user/createWithArray?user=1", fullUrlWithParams); + } + + @Test + public void testBuildFromApiSampleMessage() { + OriginalHttpRequest originalHttpRequest = new OriginalHttpRequest(); + originalHttpRequest.buildFromApiSampleMessage("{\"request\":{\"url\":\"https://petstore.swagger.io/v2/pet/1\",\"method\":\"POST\",\"type\":\"HTTP/2\",\"queryParams\":null,\"body\":\"name=asd&status=available\",\"headers\":\"{\\\"sec-fetch-mode\\\":\\\"cors\\\",\\\"referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"content-length\\\":\\\"25\\\",\\\"sec-fetch-site\\\":\\\"same-origin\\\",\\\"accept-language\\\":\\\"en-US,en;q=0.5\\\",\\\"origin\\\":\\\"https://petstore.swagger.io\\\",\\\"accept\\\":\\\"application/json\\\",\\\"te\\\":\\\"trailers\\\",\\\"host\\\":\\\"null\\\",\\\"connection\\\":\\\"keep-alive\\\",\\\"content-type\\\":\\\"application/x-www-form-urlencoded\\\",\\\"accept-encoding\\\":\\\"gzip, deflate, br\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"sec-fetch-dest\\\":\\\"empty\\\"}\"},\"response\":{\"statusCode\":404,\"body\":\"{\\\"code\\\":404,\\\"type\\\":\\\"unknown\\\",\\\"message\\\":\\\"not found\\\"}\",\"headers\":\"{\\\"date\\\":\\\"Wed, 31 Aug 2022 15:30:05 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\"}}"); + + assertEquals("https://petstore.swagger.io/v2/pet/1", originalHttpRequest.getUrl()); + assertEquals("HTTP/2", originalHttpRequest.getType()); + assertNull(originalHttpRequest.getQueryParams()); + assertEquals("POST", originalHttpRequest.getMethod()); + assertEquals("name=asd&status=available", originalHttpRequest.getBody()); + + + originalHttpRequest.buildFromApiSampleMessage("{\"request\":{\"url\":\"https://bat.bing.com:443/actionp/0\",\"method\":\"POST\",\"type\":\"HTTP/2\",\"queryParams\":\"ti=4063247&tm=gtm002&Ver=2&mid=ca2d781b-4525-475c-a8dc-38497d845979&sid=0910c9d0275811eda8eb6b80b2a40e19&vid=ba436bb022bb11edb0d817c5213a1c13&vids=0&msclkid=N&evt=pageHide\",\"body\":\"{}\",\"headers\":\"{\\\"sec-fetch-mode\\\":\\\"no-cors\\\",\\\"referer\\\":\\\"https://www.atlassian.com/\\\",\\\"content-length\\\":\\\"0\\\",\\\"sec-fetch-site\\\":\\\"cross-site\\\",\\\"cookie\\\":\\\"null\\\",\\\"accept-language\\\":\\\"en-GB,en-US;q=0.9,en;q=0.8\\\",\\\"origin\\\":\\\"https://www.atlassian.com\\\",\\\"accept\\\":\\\"*/*\\\",\\\"sec-ch-ua\\\":\\\"\\\\\\\" Not A;Brand\\\\\\\";v=\\\\\\\"99\\\\\\\", \\\\\\\"Chromium\\\\\\\";v=\\\\\\\"104\\\\\\\"\\\",\\\"sec-ch-ua-mobile\\\":\\\"?0\\\",\\\"sec-ch-ua-platform\\\":\\\"\\\\\\\"macOS\\\\\\\"\\\",\\\"host\\\":\\\"bat.bing.com\\\",\\\"accept-encoding\\\":\\\"gzip, deflate\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36\\\",\\\"sec-fetch-dest\\\":\\\"empty\\\"}\"},\"response\":{\"statusCode\":204,\"body\":\"\",\"headers\":\"{\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"date\\\":\\\"Wed, 31 Aug 2022 08:45:32 GMT\\\",\\\"set-cookie\\\":\\\"MUID=267C033E0C226A691693112F0D8A6BB9; domain=.bing.com; expires=Mon, 25-Sep-2023 08:45:32 GMT; path=/; SameSite=None; Secure; Priority=High;;MR=0; domain=bat.bing.com; expires=Wed, 07-Sep-2022 08:45:32 GMT; path=/; SameSite=None; Secure;\\\",\\\"expires\\\":\\\"Fri, 01 Jan 1990 00:00:00 GMT\\\",\\\"x-msedge-ref\\\":\\\"Ref A: C9B74F2BDB31442C9060CF9E117258DC Ref B: BOM01EDGE1114 Ref C: 2022-08-31T08:45:32Z\\\",\\\"x-cache\\\":\\\"CONFIG_NOCACHE\\\",\\\"cache-control\\\":\\\"no-cache, must-revalidate\\\",\\\"pragma\\\":\\\"no-cache\\\",\\\"strict-transport-security\\\":\\\"max-age=31536000; includeSubDomains; preload\\\",\\\"accept-ch\\\":\\\"Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version\\\"}\"}}"); + + assertEquals("https://bat.bing.com:443/actionp/0", originalHttpRequest.getUrl()); + assertEquals("HTTP/2", originalHttpRequest.getType()); + assertEquals("ti=4063247&tm=gtm002&Ver=2&mid=ca2d781b-4525-475c-a8dc-38497d845979&sid=0910c9d0275811eda8eb6b80b2a40e19&vid=ba436bb022bb11edb0d817c5213a1c13&vids=0&msclkid=N&evt=pageHide",originalHttpRequest.getQueryParams()); + assertEquals("POST", originalHttpRequest.getMethod()); + assertEquals("{}", originalHttpRequest.getBody()); + + + + originalHttpRequest.buildFromApiSampleMessage("{\"request\":{\"url\":\"https://veviwag1.atlassian.net:443/rest/api/latest/user/properties/help_button_ui_state\",\"method\":\"PUT\",\"type\":\"HTTP/2\",\"queryParams\":\"accountId=6304df219a460a36a1edb0e0\",\"body\":\"{\\\"helpPanelMenu\\\":{\\\"releaseNotesNotifications\\\":0}}\",\"headers\":\"{\\\"sec-fetch-mode\\\":\\\"cors\\\",\\\"referer\\\":\\\"https://veviwag1.atlassian.net/jira/core/projects/SOM/board\\\",\\\"content-length\\\":\\\"49\\\",\\\"sec-fetch-site\\\":\\\"same-origin\\\",\\\"cookie\\\":\\\"null\\\",\\\"accept-language\\\":\\\"en-GB,en-US;q=0.9,en;q=0.8\\\",\\\"origin\\\":\\\"https://veviwag1.atlassian.net\\\",\\\"accept\\\":\\\"application/json,text/javascript,*/*\\\",\\\"sec-ch-ua\\\":\\\"\\\\\\\" Not A;Brand\\\\\\\";v=\\\\\\\"99\\\\\\\", \\\\\\\"Chromium\\\\\\\";v=\\\\\\\"104\\\\\\\"\\\",\\\"sec-ch-ua-mobile\\\":\\\"?0\\\",\\\"x-atlassian-force-account-id\\\":\\\"true\\\",\\\"sec-ch-ua-platform\\\":\\\"\\\\\\\"macOS\\\\\\\"\\\",\\\"host\\\":\\\"veviwag1.atlassian.net\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"accept-encoding\\\":\\\"gzip, deflate\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36\\\",\\\"sec-fetch-dest\\\":\\\"empty\\\"}\"},\"response\":{\"statusCode\":401,\"body\":\"{\\\"message\\\":\\\"Client must be authenticated to access this resource.\\\",\\\"status-code\\\":401}\",\"headers\":\"{\\\"date\\\":\\\"Wed, 31 Aug 2022 08:44:37 GMT\\\",\\\"server\\\":\\\"globaledge-envoy\\\",\\\"x-envoy-upstream-service-time\\\":\\\"204\\\",\\\"vary\\\":\\\"Accept\\\",\\\"www-authenticate\\\":\\\"OAuth realm=\\\\\\\"https%3A%2F%2Fveviwag1.atlassian.net\\\\\\\"\\\",\\\"expect-ct\\\":\\\"report-uri=\\\\\\\"https://web-security-reports.services.atlassian.com/expect-ct-report/atlassian-proxy\\\\\\\", max-age=86400\\\",\\\"strict-transport-security\\\":\\\"max-age=63072000; preload\\\",\\\"atl-traceid\\\":\\\"10343bd6888cf756\\\",\\\"set-cookie\\\":\\\"atlassian.xsrf.token=825dbd3d-a44d-45db-9409-60981db9b8e0_b1e600497cbaa9996a966cc075023d2054fd4954_lout; path=/; SameSite=None; Secure\\\",\\\"x-content-type-options\\\":\\\"nosniff\\\",\\\"x-xss-protection\\\":\\\"1; mode=block\\\",\\\"nel\\\":\\\"{\\\\\\\"failure_fraction\\\\\\\": 0.001, \\\\\\\"include_subdomains\\\\\\\": true, \\\\\\\"max_age\\\\\\\": 600, \\\\\\\"report_to\\\\\\\": \\\\\\\"endpoint-1\\\\\\\"}\\\",\\\"x-arequestid\\\":\\\"7bc7c45d-5342-4379-a63c-37116bc4f8d0\\\",\\\"content-type\\\":\\\"application/json;charset=UTF-8\\\",\\\"timing-allow-origin\\\":\\\"*\\\",\\\"report-to\\\":\\\"{\\\\\\\"endpoints\\\\\\\": [{\\\\\\\"url\\\\\\\": \\\\\\\"https://dz8aopenkvv6s.cloudfront.net\\\\\\\"}], \\\\\\\"group\\\\\\\": \\\\\\\"endpoint-1\\\\\\\", \\\\\\\"include_subdomains\\\\\\\": true, \\\\\\\"max_age\\\\\\\": 600}\\\",\\\"cache-control\\\":\\\"no-transform\\\"}\"}}"); + + assertEquals("https://veviwag1.atlassian.net:443/rest/api/latest/user/properties/help_button_ui_state", originalHttpRequest.getUrl()); + assertEquals("HTTP/2", originalHttpRequest.getType()); + assertEquals("accountId=6304df219a460a36a1edb0e0",originalHttpRequest.getQueryParams()); + assertEquals("PUT", originalHttpRequest.getMethod()); + assertEquals("{\"helpPanelMenu\":{\"releaseNotesNotifications\":0}}", originalHttpRequest.getBody()); + } + + @Test + public void testCopy() { + String url = "url"; + String queryParams = "user=avneesh"; + String method = "GET"; + String body = "{}"; + Map> headers = new HashMap<>(); + headers.put("something", Collections.singletonList("value")); + String type = "HTTP 1.1"; + + OriginalHttpRequest originalHttpRequest = new OriginalHttpRequest( + url, queryParams, method, body, headers, type + ); + + OriginalHttpRequest copyOriginalHttpRequest = originalHttpRequest.copy(); + + assertEquals(url,copyOriginalHttpRequest.getUrl()); + assertEquals(queryParams,copyOriginalHttpRequest.getQueryParams()); + assertEquals(method,copyOriginalHttpRequest.getMethod()); + assertEquals(body,copyOriginalHttpRequest.getBody()); + assertEquals(headers,copyOriginalHttpRequest.getHeaders()); + assertEquals(type,copyOriginalHttpRequest.getType()); + + } + + @Test + public void testCombineQueryParams() { + String query1 = "user=avneesh&age=99&favColour=blue&status=all_is_well"; + String query2 = "status=blah%20blah&age=101"; + String combinedQuery = OriginalHttpRequest.combineQueryParams(query1, query2); + assertTrue(combinedQuery.contains("status=blah%20blah")); + + BasicDBObject combinedQueryObject = RequestTemplate.getQueryJSON("google.com?"+combinedQuery); + + assertEquals("avneesh", combinedQueryObject.get("user")); + assertEquals("101", combinedQueryObject.get("age")); + assertEquals("blue", combinedQueryObject.get("favColour")); + assertEquals("blah blah", combinedQueryObject.get("status")); + } + + @Test + public void testGetRawQueryFromJson() { + String normalReq = "{\"name\": \"avneesh\", \"cities\": [{\"name\": \"Mumbai\"}, {\"name\": \"Bangalore\"}], \"age\": 99}"; + String resultNormalReq = OriginalHttpRequest.getRawQueryFromJson(normalReq); + BasicDBObject queryParams = RequestTemplate.getQueryJSON("?"+ resultNormalReq); + assertEquals(3, queryParams.size()); + } + +} diff --git a/libs/dao/src/test/java/com/akto/dto/TestOriginalHttpResponse.java b/libs/dao/src/test/java/com/akto/dto/TestOriginalHttpResponse.java new file mode 100644 index 0000000000..969c7ca3f0 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/TestOriginalHttpResponse.java @@ -0,0 +1,45 @@ +package com.akto.dto; + +import org.junit.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class TestOriginalHttpResponse { + + @Test + public void testBuildFromSampleMessage() { + OriginalHttpResponse originalHttpResponse = new OriginalHttpResponse(); + String message = "{\"method\":\"POST\",\"requestPayload\":\"[\\n {\\n \\\"id\\\": 0,\\n \\\"username\\\": \\\"string\\\",\\n \\\"firstName\\\": \\\"string\\\",\\n \\\"lastName\\\": \\\"string\\\",\\n \\\"email\\\": \\\"string\\\",\\n \\\"password\\\": \\\"string\\\",\\n \\\"phone\\\": \\\"string\\\",\\n \\\"userStatus\\\": 0\\n }\\n]\",\"responsePayload\":\"{\\\"code\\\":200,\\\"type\\\":\\\"unknown\\\",\\\"message\\\":\\\"ok\\\"}\",\"ip\":\"null\",\"source\":\"HAR\",\"type\":\"HTTP/2\",\"akto_vxlan_id\":\"1661807253\",\"path\":\"https://petstore.swagger.io/v2/user/createWithArray?user=1\",\"requestHeaders\":\"{\\\"Origin\\\":\\\"https://petstore.swagger.io\\\",\\\"Accept\\\":\\\"application/json\\\",\\\"User-Agent\\\":\\\"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\\\",\\\"Referer\\\":\\\"https://petstore.swagger.io/\\\",\\\"Connection\\\":\\\"keep-alive\\\",\\\"Sec-Fetch-Dest\\\":\\\"empty\\\",\\\"Sec-Fetch-Site\\\":\\\"same-origin\\\",\\\"Host\\\":\\\"petstore.swagger.io\\\",\\\"Accept-Encoding\\\":\\\"gzip, deflate, br\\\",\\\"Sec-Fetch-Mode\\\":\\\"cors\\\",\\\"TE\\\":\\\"trailers\\\",\\\"Accept-Language\\\":\\\"en-US,en;q=0.5\\\",\\\"Content-Length\\\":\\\"195\\\",\\\"Content-Type\\\":\\\"application/json\\\"}\",\"responseHeaders\":\"{\\\"date\\\":\\\"Tue, 04 Jan 2022 20:14:27 GMT\\\",\\\"access-control-allow-origin\\\":\\\"*\\\",\\\"server\\\":\\\"Jetty(9.2.9.v20150224)\\\",\\\"access-control-allow-headers\\\":\\\"Content-Type, api_key, Authorization\\\",\\\"X-Firefox-Spdy\\\":\\\"h2\\\",\\\"content-type\\\":\\\"application/json\\\",\\\"access-control-allow-methods\\\":\\\"GET, POST, DELETE, PUT\\\"}\",\"time\":\"1641327267\",\"contentType\":\"application/json\",\"akto_account_id\":\"1000000\",\"statusCode\":\"200\",\"status\":\"OK\"}"; + originalHttpResponse.buildFromSampleMessage(message); + + assertEquals(200, originalHttpResponse.getStatusCode()); + assertEquals(7, originalHttpResponse.getHeaders().size()); + assertEquals("{\"code\":200,\"type\":\"unknown\",\"message\":\"ok\"}", originalHttpResponse.getBody()); + + + } + + @Test + public void testCopy() { + int statusCode = 201; + String body = "{}"; + Map> headers = new HashMap<>(); + headers.put("something", Collections.singletonList("value")); + + OriginalHttpResponse originalHttpResponse = new OriginalHttpResponse( + body, headers, statusCode + ); + + OriginalHttpResponse copyOriginalHttpResponse= originalHttpResponse.copy(); + + assertEquals(body, copyOriginalHttpResponse.getBody()); + assertEquals(headers, copyOriginalHttpResponse.getHeaders()); + assertEquals(statusCode, copyOriginalHttpResponse.getStatusCode()); + + } +} diff --git a/libs/dao/src/test/java/com/akto/dto/api_workflow/TestGraph.java b/libs/dao/src/test/java/com/akto/dto/api_workflow/TestGraph.java new file mode 100644 index 0000000000..f330a442d6 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/api_workflow/TestGraph.java @@ -0,0 +1,87 @@ +package com.akto.dto.api_workflow; + +import com.akto.dto.testing.WorkflowNodeDetails; +import com.akto.dto.testing.WorkflowTest; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +public class TestGraph { + + @Test + public void testBuildGraph() { + Graph graph = new Graph(); + + WorkflowTest workflowTest = new WorkflowTest(); + + Map mapNodeIdToWorkflowNodeDetails = new HashMap<>(); + WorkflowNodeDetails x1 = new WorkflowNodeDetails(); + mapNodeIdToWorkflowNodeDetails.put("x1", x1); + WorkflowNodeDetails x1659328241 = new WorkflowNodeDetails(); + mapNodeIdToWorkflowNodeDetails.put("x1659328241", x1659328241); + workflowTest.setMapNodeIdToWorkflowNodeDetails(mapNodeIdToWorkflowNodeDetails); + + List edges = new ArrayList<>(); + edges.add("{\"source\":\"1\",\"target\":\"x1\",\"id\":\"x1\",\"selected\":false,\"markerEnd\":{\"type\":\"arrow\"}}"); + edges.add("{\"source\":\"x1659328241\",\"target\":\"3\",\"id\":\"x1659328245\",\"selected\":true,\"markerEnd\":{\"type\":\"arrow\"}}"); + edges.add("{\"source\":\"x1\",\"target\":\"x1659328241\",\"id\":\"x1659328241\",\"selected\":false,\"markerEnd\":{\"type\":\"arrow\"}}"); + workflowTest.setEdges(edges); + + graph.buildGraph(workflowTest); + + Map nodes = graph.getNodes(); + assertEquals(2, nodes.size()); + assertNotNull(nodes.get("x1")); + assertNotNull(nodes.get("x1659328241")); + + List sortedNodes = graph.sort(); + } + + @Test + public void testSort() { + Graph graph = new Graph(); + Node node1 = new Node("1", new WorkflowNodeDetails()); + Node node2 = new Node("2", new WorkflowNodeDetails()); + Node node3 = new Node("3", new WorkflowNodeDetails()); + Node node4 = new Node("4", new WorkflowNodeDetails()); + Node node5 = new Node("5", new WorkflowNodeDetails()); + Node node6 = new Node("6", new WorkflowNodeDetails()); + Node node7 = new Node("7", new WorkflowNodeDetails()); + Node node8 = new Node("8", new WorkflowNodeDetails()); + Node node9 = new Node("9", new WorkflowNodeDetails()); + Node node10 = new Node("10", new WorkflowNodeDetails()); + + // all arrows move from left to right (1->2->4->5->9 and 7) + // 2 -- 4 -- 5---------------9 + // / \ \ + // / \ 10 + // 1 __/ \__7___8_______/ + // | \ / | + // | \ / | + // | 3 ------- 6 | + // |_____________________| + // + + node1.addNeighbours(node2.getId(), node3.getId(), node7.getId()); + node2.addNeighbours(node4.getId()); + node4.addNeighbours(node5.getId()); + node5.addNeighbours(node7.getId(), node9.getId()); + node9.addNeighbours(node10.getId()); + node3.addNeighbours(node6.getId()); + node6.addNeighbours(node7.getId()); + node7.addNeighbours(node8.getId()); + node8.addNeighbours(node10.getId()); + + graph.addNodes(node7, node8, node9, node10, node1, node2, node3, node4, node5, node6); + + + List nodes = graph.sort(); + } + + +} diff --git a/libs/dao/src/test/java/com/akto/dto/data_types/TestCustomDataType.java b/libs/dao/src/test/java/com/akto/dto/data_types/TestCustomDataType.java new file mode 100644 index 0000000000..89cf1c540c --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/data_types/TestCustomDataType.java @@ -0,0 +1,83 @@ +package com.akto.dto.data_types; + +import com.akto.dto.CustomDataType; +import com.akto.dto.IgnoreData; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TestCustomDataType { + + + @Test + public void testEmptyPredicatesValidate() { + Conditions conditions1 = new Conditions(new ArrayList<>(), Conditions.Operator.AND); + boolean r = conditions1.validate("something"); + assertFalse(r); + } + + @Test + public void testValidate() { + List predicateList1 = Arrays.asList(new StartsWithPredicate("w13rd"), new EndsWithPredicate("a55")); + Conditions conditions1 = new Conditions(predicateList1, Conditions.Operator.AND); + + List predicateList2 = Collections.singletonList(new StartsWithPredicate("Dope")); + Conditions conditions2 = new Conditions(predicateList2, Conditions.Operator.AND); + IgnoreData ignoreData = new IgnoreData(new HashMap<>(), new HashSet<>()); + + CustomDataType customDataType = new CustomDataType("name", true, Collections.emptyList(), + 0,true, conditions2, conditions1, Conditions.Operator.AND,ignoreData); + + assertTrue(customDataType.validate("w13rd_something_a55", "Dope_shit")); + assertFalse(customDataType.validate("w13rd_something_a55", "BDope_shit")); + assertFalse(customDataType.validate("Bw13rd_something_a55", "Dope_shit")); + assertFalse(customDataType.validate("Bw13rd_something_a55", "BDope_shit")); + + customDataType.setOperator(Conditions.Operator.OR); + assertTrue(customDataType.validate("w13rd_something_a55", "Dope_shit")); + assertTrue(customDataType.validate("w13rd_something_a55", "BDope_shit")); + assertTrue(customDataType.validate("Bw13rd_something_a55", "Dope_shit")); + assertFalse(customDataType.validate("Bw13rd_something_a55", "BDope_shit")); + + customDataType.setOperator(Conditions.Operator.AND); + customDataType.setValueConditions(null); + assertTrue(customDataType.validate("w13rd_something_a55", "Dope_shit")); + assertFalse(customDataType.validate("w13rd_something_a55", "BDope_shit")); + assertTrue(customDataType.validate("Bw13rd_something_a55", "Dope_shit")); + assertFalse(customDataType.validate("Bw13rd_something_a55", "BDope_shit")); + + customDataType.setValueConditions(conditions1); + customDataType.setKeyConditions(null); + assertTrue(customDataType.validate("w13rd_something_a55", "Dope_shit")); + assertTrue(customDataType.validate("w13rd_something_a55", "BDope_shit")); + assertFalse(customDataType.validate("Bw13rd_something_a55", "Dope_shit")); + assertFalse(customDataType.validate("Bw13rd_something_a55", "BDope_shit")); + } + + @Test + public void testValidOfConditions() { + List predicateList = Arrays.asList(new StartsWithPredicate("w13rd"), new EndsWithPredicate("a55")); + + Conditions conditions = new Conditions(predicateList, Conditions.Operator.OR); + assertTrue(conditions.validate("w13rd_something_a55")); + + conditions = new Conditions(predicateList, Conditions.Operator.OR); + assertTrue(conditions.validate("w13brd_something_a55")); + + conditions = new Conditions(predicateList, Conditions.Operator.AND); + assertFalse(conditions.validate("w13brd_something_a55")); + + conditions = new Conditions(predicateList, Conditions.Operator.OR); + assertFalse(conditions.validate("w13brd_something_a5w5")); + } + +} diff --git a/libs/dao/src/test/java/com/akto/dto/data_types/TestPredicates.java b/libs/dao/src/test/java/com/akto/dto/data_types/TestPredicates.java new file mode 100644 index 0000000000..87ea092c8d --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/data_types/TestPredicates.java @@ -0,0 +1,280 @@ +package com.akto.dto.data_types; + +import com.akto.dto.ApiInfo; +import com.akto.dto.type.URLMethods; +import org.junit.Test; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.*; + +public class TestPredicates { + + + @Test + public void testStartsWithPredicate() { + StartsWithPredicate startsWithPredicate = new StartsWithPredicate("w13rd"); + Object value = "w13rd_a55"; + boolean result = startsWithPredicate.validate(value); + assertTrue(result); + + value = "ww13rd_a55"; + result = startsWithPredicate.validate(value); + assertFalse(result); + + value = "W13rd_a55"; + result = startsWithPredicate.validate(value); + assertFalse(result); + + value = null; + result = startsWithPredicate.validate(value); + assertFalse(result); + + value = 234; + result = startsWithPredicate.validate(value); + assertFalse(result); + + value = new HashMap(); + result = startsWithPredicate.validate(value); + assertFalse(result); + } + + @Test + public void testEndsWithPredicate() { + EndsWithPredicate endsWithPredicate = new EndsWithPredicate("a55"); + Object value = "w13rd_a55"; + boolean result = endsWithPredicate.validate(value); + assertTrue(result); + + value = "w13rd_a553"; + result = endsWithPredicate.validate(value); + assertFalse(result); + + value = null; + result = endsWithPredicate.validate(value); + assertFalse(result); + + value = 234; + result = endsWithPredicate.validate(value); + assertFalse(result); + + value = new HashMap(); + result = endsWithPredicate.validate(value); + assertFalse(result); + } + + @Test + public void testRegexPredicate() { + RegexPredicate regexPredicate = new RegexPredicate("^\\d{3}-\\d{2}-\\d{4}$"); + Object value = "234-21-2342"; + boolean result = regexPredicate.validate(value); + assertTrue(result); + + value = "w13rd_a55"; + result = regexPredicate.validate(value); + assertFalse(result); + + value = null; + result = regexPredicate.validate(value); + assertFalse(result); + + value = 234; + result = regexPredicate.validate(value); + assertFalse(result); + value = new HashMap(); + result = regexPredicate.validate(value); + assertFalse(result); + } + + @Test + public void testIsNumberPredicate() { + IsNumberPredicate isNumberPredicate = new IsNumberPredicate(); + Object value = 32; + boolean result = isNumberPredicate.validate(value); + assertTrue(result); + + value = 32.3; + result = isNumberPredicate.validate(value); + assertTrue(result); + + result = isNumberPredicate.validate(null); + assertFalse(result); + + value = "1234"; + result = isNumberPredicate.validate(value); + assertFalse(result); + + value = new HashMap<>(); + result = isNumberPredicate.validate(value); + assertFalse(result); + } + + @Test + public void testEqualsToPredicate() { + EqualsToPredicate equalsToPredicate = new EqualsToPredicate("something"); + Object value = "something"; + boolean result = equalsToPredicate.validate(value); + assertTrue(result); + + value = " something "; + result = equalsToPredicate.validate(value); + assertTrue(result); + + result = equalsToPredicate.validate(null); + assertFalse(result); + + value = "Avneesh"; + result = equalsToPredicate.validate(value); + assertFalse(result); + + value = new HashMap<>(); + result = equalsToPredicate.validate(value); + assertFalse(result); + } + + @Test + public void testContainsPredicate() { + ContainsPredicate containsPredicate = new ContainsPredicate("contains"); + Object value = "thiscONTainscontains"; + boolean result = containsPredicate.validate(value); + assertTrue(result); + + value = "doesNotCONTAINS"; + result = containsPredicate.validate(value); + assertFalse(result); + + value = new ApiInfo.ApiInfoKey( + 1234, + "containsUrl", + URLMethods.Method.GET + ); + result = containsPredicate.validate(value); + assertTrue(result); + + value = new ApiInfo.ApiInfoKey( + 1234, + "doesNotCONTAINSurl", + URLMethods.Method.GET + ); + result = containsPredicate.validate(value); + assertFalse(result); + result = containsPredicate.validate(null); + assertFalse(result); + } + + @Test + public void testBelongsToPredicate() { + Set apiInfoKeySet = new HashSet<>(); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/admin", + URLMethods.Method.GET + )); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/admin/changeMethod", + URLMethods.Method.GET + )); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/billing", + URLMethods.Method.GET + )); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/billing/generateReport", + URLMethods.Method.GET + )); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/user", + URLMethods.Method.POST + )); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/user/login", + URLMethods.Method.GET + )); + BelongsToPredicate belongsToPredicate = new BelongsToPredicate(apiInfoKeySet); + Object value = "false"; + boolean result = belongsToPredicate.validate(value); + assertFalse(result); + + value = new ApiInfo.ApiInfoKey( + 1234, + "/user/login", + URLMethods.Method.GET + ); + result = belongsToPredicate.validate(value); + assertTrue(result); + + value = new ApiInfo.ApiInfoKey( + 12345, + "/user/login", + URLMethods.Method.GET + ); + result = belongsToPredicate.validate(value); + assertFalse(result); + result = belongsToPredicate.validate(null); + assertFalse(result); + } + + @Test + public void testDoesNotBelongsToPredicate() { + Set apiInfoKeySet = new HashSet<>(); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/admin", + URLMethods.Method.GET + )); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/admin/changeMethod", + URLMethods.Method.GET + )); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/billing", + URLMethods.Method.GET + )); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/billing/generateReport", + URLMethods.Method.GET + )); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/user", + URLMethods.Method.POST + )); + apiInfoKeySet.add(new ApiInfo.ApiInfoKey( + 1234, + "/user/login", + URLMethods.Method.GET + )); + NotBelongsToPredicate notBelongsToPredicate = new NotBelongsToPredicate(apiInfoKeySet); + Object value = "false"; + boolean result = notBelongsToPredicate.validate(value); + assertTrue(result); + + value = new ApiInfo.ApiInfoKey( + 1234, + "/user/login", + URLMethods.Method.GET + ); + result = notBelongsToPredicate.validate(value); + assertFalse(result); + + value = new ApiInfo.ApiInfoKey( + 12345, + "/user/login", + URLMethods.Method.GET + ); + result = notBelongsToPredicate.validate(value); + assertTrue(result); + result = notBelongsToPredicate.validate(null); + assertTrue(result); + } +} diff --git a/libs/dao/src/test/java/com/akto/dto/runtime_filters/TestFieldExistsFilter.java b/libs/dao/src/test/java/com/akto/dto/runtime_filters/TestFieldExistsFilter.java new file mode 100644 index 0000000000..1c58d79b2c --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/runtime_filters/TestFieldExistsFilter.java @@ -0,0 +1,40 @@ +package com.akto.dto.runtime_filters; + +import com.akto.dto.HttpResponseParams; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TestFieldExistsFilter { + + @Test + public void happy() { + FieldExistsFilter fieldExistsFilter = new FieldExistsFilter("user_id"); + HttpResponseParams httpResponseParams1 = new HttpResponseParams(); + httpResponseParams1.setPayload("{ \"name\": \"akto\", \"car\": \"Merc\", \"user\": { \"user_id\": 1, \"name\": \"akto\", \"age\": 3} }"); + boolean result1 = fieldExistsFilter.process(httpResponseParams1); + assertTrue(result1); + + httpResponseParams1.setPayload("{ \"name\": \"akto\", \"car\": \"Merc\", \"user\": { \"user\": 1, \"name\": \"akto\", \"age\": 3} }"); + boolean result2 = fieldExistsFilter.process(httpResponseParams1); + assertFalse(result2); + + + httpResponseParams1.setPayload("{ \"name\": \"akto\", \"car\": \"Merc\", \"user\": [{ \"userId\": 1, \"name\": \"akto\", \"age\": 3}, { \"userId\": 1, \"name\": \"akto\", \"age\": 3},{ \"user_id\": 1, \"name\": \"akto\", \"age\": 3}] }"); + boolean result3 = fieldExistsFilter.process(httpResponseParams1); + assertTrue(result3); + + httpResponseParams1.setPayload("{ \"name\": \"akto\", \"car\": \"Merc\", \"user_id\": { \"user\": 1, \"name\": \"akto\", \"age\": 3} }"); + boolean result4 = fieldExistsFilter.process(httpResponseParams1); + assertTrue(result4); + + httpResponseParams1.setPayload("{ \"name\": \"akto\", \"car\": \"Merc\", \"user\": [[{ \"userId\": 1, \"name\": \"akto\", \"age\": 3}, { \"userId\": 1, \"name\": \"akto\", \"age\": 3},{ \"user_id\": 1, \"name\": \"akto\", \"age\": 3}]] }"); + boolean result5 = fieldExistsFilter.process(httpResponseParams1); + assertTrue(result5); + + httpResponseParams1.setPayload("[{ \"name\": \"akto\", \"car\": \"Merc\", \"user\": [[{ \"userId\": 1, \"name\": \"akto\", \"age\": 3}, { \"userId\": 1, \"name\": \"akto\", \"age\": 3},{ \"user_id\": 1, \"name\": \"akto\", \"age\": 3}]] }]"); + boolean result6 = fieldExistsFilter.process(httpResponseParams1); + assertTrue(result6); + } +} diff --git a/libs/dao/src/test/java/com/akto/dto/runtime_filters/TestResponseCodeRuntimeFilter.java b/libs/dao/src/test/java/com/akto/dto/runtime_filters/TestResponseCodeRuntimeFilter.java new file mode 100644 index 0000000000..56cd2a3415 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/runtime_filters/TestResponseCodeRuntimeFilter.java @@ -0,0 +1,36 @@ +package com.akto.dto.runtime_filters; + +import com.akto.dto.HttpResponseParams; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class TestResponseCodeRuntimeFilter { + + @Test + public void happy() { + ResponseCodeRuntimeFilter responseCodeRuntimeFilter = new ResponseCodeRuntimeFilter(200,299); + + HttpResponseParams httpResponseParams1 = new HttpResponseParams(); + httpResponseParams1.statusCode = 233; + boolean result1 = responseCodeRuntimeFilter.process(httpResponseParams1); + assertTrue(result1); + + HttpResponseParams httpResponseParams2 = new HttpResponseParams(); + httpResponseParams2.statusCode = 200; + boolean result2 = responseCodeRuntimeFilter.process(httpResponseParams2); + assertTrue(result2); + + HttpResponseParams httpResponseParams3 = new HttpResponseParams(); + httpResponseParams3.statusCode = 299; + boolean result3 = responseCodeRuntimeFilter.process(httpResponseParams3); + assertTrue(result3); + + HttpResponseParams httpResponseParams4 = new HttpResponseParams(); + httpResponseParams4.statusCode = 333; + boolean result4 = responseCodeRuntimeFilter.process(httpResponseParams4); + assertFalse(result4); + + } +} diff --git a/libs/dao/src/test/java/com/akto/dto/testing/AuthMechanismTests.java b/libs/dao/src/test/java/com/akto/dto/testing/AuthMechanismTests.java new file mode 100644 index 0000000000..0e41b931b7 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/testing/AuthMechanismTests.java @@ -0,0 +1,122 @@ +package com.akto.dto.testing; + +import com.akto.dto.HttpRequestParams; +import com.akto.dto.OriginalHttpRequest; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; + +public class AuthMechanismTests { + + private void validate(OriginalHttpRequest request, String key, List modifiedValue) { + String value = "Value"; + String finalKey = key.toLowerCase().trim(); + AuthMechanism authMechanism = new AuthMechanism(); + authMechanism.setAuthParams(Collections.singletonList(new HardcodedAuthParam(AuthParam.Location.HEADER, key, value, true))); + + boolean result = authMechanism.addAuthToRequest(request); + assertEquals(result, modifiedValue!=null); + List modifiedHeader = request.getHeaders().get(finalKey); + assertEquals(modifiedHeader, modifiedValue); + + result = authMechanism.removeAuthFromRequest(request); + assertEquals(result, modifiedValue!=null); + if (modifiedValue == null) return; + modifiedHeader = request.getHeaders().get(finalKey); + assertEquals(modifiedHeader, Collections.singletonList(null)); + } + + private void validateBodyAuthOperations(OriginalHttpRequest request, String key, String modifiedValue, String removeExpectedValue, Boolean modified, Boolean hardcoded) { + String value = "Value"; + String finalKey = key.toLowerCase().trim(); + AuthMechanism authMechanism = new AuthMechanism(); + if (hardcoded) { + authMechanism.setAuthParams(Collections.singletonList(new HardcodedAuthParam(AuthParam.Location.BODY, key, value, false))); + } else { + authMechanism.setAuthParams(Collections.singletonList(new LoginRequestAuthParam(AuthParam.Location.BODY, key, value, false))); + } + + boolean result = authMechanism.addAuthToRequest(request); + assertEquals(result, modified); + + String body = request.getBody(); + assertEquals(request.getBody(), modifiedValue); + + result = authMechanism.removeAuthFromRequest(request); + assertEquals(result, modified); + if (modifiedValue == null) return; + + body = request.getBody(); + assertEquals(body, removeExpectedValue); + } + + @Test + public void testAddAuthToRequestHardcoded() { + Map> headers = new HashMap<>(); + headers.put("header1", Arrays.asList("1","2")); + headers.put("header2", null); + headers.put("header3", Collections.emptyList()); + headers.put("header4", Collections.singletonList("1")); + + OriginalHttpRequest request = new OriginalHttpRequest("", "", "GET", "",headers, "HTTP/1.1"); + + validate(request, "Header1", Collections.singletonList("Value")); + validate(request, " Header2 ", Collections.singletonList("Value")); + validate(request, "InvalidHeader", null); + + + } + + @Test + public void testAddAuthToRequestBodyHardcoded() { + Map> headers = new HashMap<>(); + headers.put("header1", Arrays.asList("1","2")); + headers.put("header2", null); + headers.put("header3", Collections.emptyList()); + headers.put("header4", Collections.singletonList("1")); + + String requestPayload = "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}"; + + OriginalHttpRequest request = new OriginalHttpRequest("", "", "GET", requestPayload,headers, "HTTP/1.1"); + + validateBodyAuthOperations(request, "initials.xy", "{\"initials\":{\"initials\":\"AH\",\"xy\":\"Value\"},\"xy\":\"ab\"}", "{\"initials\":{\"initials\":\"AH\",\"xy\":null},\"xy\":\"ab\"}", true, true); + request.setBody(requestPayload); + validateBodyAuthOperations(request, "xy", "{\"initials\":{\"initials\":\"AH\",\"xy\":\"ab\"},\"xy\":\"Value\"}", "{\"initials\":{\"initials\":\"AH\",\"xy\":\"ab\"},\"xy\":null}", true, true); + request.setBody(requestPayload); + validateBodyAuthOperations(request, "initials.yz", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", false, true); + request.setBody(requestPayload); + validateBodyAuthOperations(request, "yz", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", false, true); + request.setBody(requestPayload); + validateBodyAuthOperations(request, "initials.xy.yz", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", false, true); + + + } + + @Test + public void testAddAuthToRequestBodyLoginFlow() { + Map> headers = new HashMap<>(); + headers.put("header1", Arrays.asList("1","2")); + headers.put("header2", null); + headers.put("header3", Collections.emptyList()); + headers.put("header4", Collections.singletonList("1")); + + String requestPayload = "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}"; + + OriginalHttpRequest request = new OriginalHttpRequest("", "", "GET", requestPayload,headers, "HTTP/1.1"); + + validateBodyAuthOperations(request, "initials.xy", "{\"initials\":{\"initials\":\"AH\",\"xy\":\"Value\"},\"xy\":\"ab\"}", "{\"initials\":{\"initials\":\"AH\",\"xy\":null},\"xy\":\"ab\"}", true, false); + request.setBody(requestPayload); + validateBodyAuthOperations(request, "xy", "{\"initials\":{\"initials\":\"AH\",\"xy\":\"ab\"},\"xy\":\"Value\"}", "{\"initials\":{\"initials\":\"AH\",\"xy\":\"ab\"},\"xy\":null}", true, false); + request.setBody(requestPayload); + validateBodyAuthOperations(request, "initials.yz", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", false, false); + request.setBody(requestPayload); + validateBodyAuthOperations(request, "yz", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", false, false); + request.setBody(requestPayload); + validateBodyAuthOperations(request, "initials.xy.yz", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", "{\"initials\": {\"initials\": \"AH\", \"xy\": \"ab\"}, \"xy\": \"ab\"}", false, false); + + + } + +} diff --git a/libs/dao/src/test/java/com/akto/dto/testing/TestLogicalGroupTestingEndpoint.java b/libs/dao/src/test/java/com/akto/dto/testing/TestLogicalGroupTestingEndpoint.java new file mode 100644 index 0000000000..62482b63da --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/testing/TestLogicalGroupTestingEndpoint.java @@ -0,0 +1,45 @@ +package com.akto.dto.testing; + +import com.akto.dto.ApiInfo; +import com.akto.dto.data_types.*; +import com.akto.dto.type.URLMethods; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TestLogicalGroupTestingEndpoint { + @Test + public void testContainsApi() { + Conditions andCondition = new Conditions(); + andCondition.setOperator(Conditions.Operator.AND); + Conditions orCondition = new Conditions(); + orCondition.setOperator(Conditions.Operator.OR); + + List andPredicates = new ArrayList<>(); + andPredicates.add(new ContainsPredicate("contains")); + Set set = new HashSet<>(); + set.add(new ApiInfo.ApiInfoKey(1234, "/admin/contains", URLMethods.Method.GET)); + andPredicates.add(new BelongsToPredicate(set)); + andCondition.setPredicates(andPredicates); + List orPredicates = new ArrayList<>(); + set.clear(); + orPredicates.add(new ContainsPredicate("contains")); + set.add(new ApiInfo.ApiInfoKey(1234, "/admin/contains", URLMethods.Method.GET)); + orPredicates.add(new NotBelongsToPredicate(set)); + orCondition.setPredicates(orPredicates); + + LogicalGroupTestingEndpoint logicalGroupTestingEndpoint = new LogicalGroupTestingEndpoint(andCondition, orCondition); + + assertTrue(logicalGroupTestingEndpoint.containsApi(new ApiInfo.ApiInfoKey(1234, "/admin/contains", URLMethods.Method.GET))); + assertFalse(logicalGroupTestingEndpoint.containsApi(null)); + logicalGroupTestingEndpoint.setAndConditions(null); + logicalGroupTestingEndpoint.setOrConditions(null); + assertFalse(logicalGroupTestingEndpoint.containsApi(new ApiInfo.ApiInfoKey(1234, "/admin/contains", URLMethods.Method.GET))); + } +} diff --git a/libs/dao/src/test/java/com/akto/dto/type/TestKeyTypes.java b/libs/dao/src/test/java/com/akto/dto/type/TestKeyTypes.java new file mode 100644 index 0000000000..15b994fef8 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/type/TestKeyTypes.java @@ -0,0 +1,106 @@ +package com.akto.dto.type; +import com.akto.dto.AktoDataType; +import com.akto.dto.CustomDataType; +import com.akto.dto.IgnoreData; +import com.akto.dto.SensitiveParamInfo; +import com.akto.dto.data_types.Conditions; +import com.akto.dto.data_types.StartsWithPredicate; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TestKeyTypes { + +public void testInitializer(){ + SingleTypeInfo.aktoDataTypeMap = new HashMap<>(); + SingleTypeInfo.aktoDataTypeMap.put("JWT", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("PHONE_NUMBER", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("CREDIT_CARD", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("IP_ADDRESS", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("EMAIL", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("SSN", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("UUID", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + } + + @Test + public void testProcess() { + testInitializer(); + String url = "url"; + String method = "GET"; + int responseCode = 200; + + Map sensitiveParamInfoBooleanMap = new HashMap<>(); + SensitiveParamInfo sensitiveParamInfo1 = new SensitiveParamInfo( + url, method, responseCode, false, "param1", 0, true + ); + + KeyTypes keyTypes = new KeyTypes(new HashMap<>(),false); + SingleTypeInfo.customDataTypeMap = new HashMap<>(); + IgnoreData ignoreData = new IgnoreData(new HashMap<>(), new HashSet<>()); + CustomDataType customDataType1 = new CustomDataType("SHIPPING", true, Collections.emptyList(), + 1, true,new Conditions(Collections.singletonList(new StartsWithPredicate("ship")), Conditions.Operator.AND),null, Conditions.Operator.AND,ignoreData); + CustomDataType customDataType2 = new CustomDataType("CAPTAIN",false, Collections.emptyList(), + 1,true,new Conditions(Collections.singletonList(new StartsWithPredicate("captain")), Conditions.Operator.AND),null, Conditions.Operator.AND,ignoreData); + + SingleTypeInfo.customDataTypeMap.put("SHIPPING", customDataType1); + SingleTypeInfo.customDataTypeMap.put("CAPTAIN", customDataType2); + SingleTypeInfo.customDataTypesSortedBySensitivity = new ArrayList<>(); + SingleTypeInfo.customDataTypesSortedBySensitivity.add(customDataType1); + SingleTypeInfo.customDataTypesSortedBySensitivity.add(customDataType2); + + // not sensitive + keyTypes.process(url,method,responseCode ,false ,"param1" ,"value1" , + "u1" ,0 ,"rawMessage1" , sensitiveParamInfoBooleanMap, false); + + assertEquals(keyTypes.occurrences.get(SingleTypeInfo.GENERIC).getExamples().size(), 0); + + // sensitive + keyTypes.process(url,method,responseCode ,false ,"param1" ,"avneesh@akto.io" , + "u1" ,0 ,"rawMessage2" , sensitiveParamInfoBooleanMap, false); + + assertEquals(keyTypes.occurrences.get(SingleTypeInfo.EMAIL).getExamples().size(), 1); + assertEquals(keyTypes.occurrences.get(SingleTypeInfo.GENERIC).getExamples().size(), 0); + + // sensitive repeat (shouldn't add more examples) + keyTypes.process(url,method,responseCode ,false ,"param1" ,"avneesh@akto.io" , + "u1" ,0 ,"rawMessage3" , sensitiveParamInfoBooleanMap, false); + + assertEquals(keyTypes.occurrences.get(SingleTypeInfo.EMAIL).getExamples().size(), 1); + + // custom data type normal + keyTypes.process(url,method,responseCode ,false ,"captain_id" ,"Kirk" , + "u1" ,0 ,"rawMessage3" , sensitiveParamInfoBooleanMap, false); + + assertEquals(keyTypes.occurrences.get(customDataType2.toSubType()).getExamples().size(), 0); + + // custom data type sensitive + keyTypes.process(url,method,responseCode ,false ,"ship_id" ,"NCC-1701" , + "u1" ,0 ,"rawMessage3" , sensitiveParamInfoBooleanMap, false); + + assertEquals(keyTypes.occurrences.get(customDataType1.toSubType()).getExamples().size(), 1); + + // custom marked sensitive + sensitiveParamInfoBooleanMap.put(sensitiveParamInfo1, false); + keyTypes.process(url,method,responseCode ,false ,"param1" ,"value1" , + "u1" ,0 ,"rawMessage1" , sensitiveParamInfoBooleanMap, false); + + assertEquals(keyTypes.occurrences.get(SingleTypeInfo.GENERIC).getExamples().size(), 1); + assertTrue(sensitiveParamInfoBooleanMap.get(sensitiveParamInfo1)); + + // custom marked sensitive repeat (shouldn't add more examples) + sensitiveParamInfoBooleanMap.put(sensitiveParamInfo1, false); + keyTypes.process(url,method,responseCode ,false ,"param1" ,"value1" , + "u1" ,0 ,"rawMessage1" , sensitiveParamInfoBooleanMap, false); + + assertEquals(keyTypes.occurrences.get(SingleTypeInfo.GENERIC).getExamples().size(), 1); + assertTrue(sensitiveParamInfoBooleanMap.get(sensitiveParamInfo1)); + + } + } diff --git a/libs/dao/src/test/java/com/akto/dto/type/TestRequestTemplate.java b/libs/dao/src/test/java/com/akto/dto/type/TestRequestTemplate.java new file mode 100644 index 0000000000..0fc5b8ca33 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/type/TestRequestTemplate.java @@ -0,0 +1,51 @@ +package com.akto.dto.type; + +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import com.akto.dto.type.SingleTypeInfo.SuperType; +import com.akto.dto.type.URLMethods.Method; + +import org.junit.Test; + +public class TestRequestTemplate { + + public RequestTemplate createRequestTemplate(boolean populateResponse) { + + Map parameters = new HashMap<>(); + Map headers = new HashMap<>(); + Map responseTemplates = new HashMap<>(); + + parameters.put("p1", new KeyTypes(new HashMap<>(), false)); + parameters.put("p2", new KeyTypes(new HashMap<>(), false)); + parameters.put("p3", new KeyTypes(new HashMap<>(), false)); + + headers.put("h1", new KeyTypes(new HashMap<>(), false)); + headers.put("h2", new KeyTypes(new HashMap<>(), false)); + headers.put("h3", new KeyTypes(new HashMap<>(), false)); + + if (populateResponse) { + responseTemplates.put(200, createRequestTemplate(false)); + } + + return new RequestTemplate(parameters, responseTemplates, headers, new TrafficRecorder(new HashMap<>())); + } + + @Test + public void testCompareKeys() { + URLTemplate urlTemplate = new URLTemplate(new String[]{""}, new SuperType[]{SuperType.STRING}, Method.POST); + assertTrue(createRequestTemplate(true).compare(createRequestTemplate(true), urlTemplate)); + assertTrue(!createRequestTemplate(false).compare(createRequestTemplate(true), urlTemplate)); + assertTrue(!createRequestTemplate(false).compare(createRequestTemplate(false), urlTemplate)); + + + RequestTemplate a = createRequestTemplate(true); + a.getHeaders().put("h4", new KeyTypes()); + assertTrue(a.compare(createRequestTemplate(true), urlTemplate)); + assertTrue(createRequestTemplate(true).compare(a, urlTemplate)); + + } + +} diff --git a/libs/dao/src/test/java/com/akto/dto/type/TestSingleTypeInfo.java b/libs/dao/src/test/java/com/akto/dto/type/TestSingleTypeInfo.java new file mode 100644 index 0000000000..6fd1d5c1e6 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/type/TestSingleTypeInfo.java @@ -0,0 +1,168 @@ +package com.akto.dto.type; + +import com.akto.dao.context.Context; +import com.akto.types.CappedSet; +import org.junit.Test; + +import java.util.HashSet; + +import static org.junit.Assert.*; + +public class TestSingleTypeInfo { + + @Test + public void testCopy() { + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId("url", "GET", 200, true, "param", SingleTypeInfo.EMAIL, 100, false); + CappedSet values = new CappedSet<>(); + values.add("ankush"); + values.add("avneesh"); + values.add("ankita"); + SingleTypeInfo singleTypeInfo = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 10,0,0,values, SingleTypeInfo.Domain.ANY, 100, 1000); + singleTypeInfo.setUniqueCount(1000); + singleTypeInfo.setPublicCount(9999); + + SingleTypeInfo singleTypeInfoCopy = singleTypeInfo.copy(); + + assertEquals(singleTypeInfo, singleTypeInfoCopy); + assertEquals(singleTypeInfo.getValues().getElements().size(), singleTypeInfoCopy.getValues().getElements().size()); + assertEquals(singleTypeInfo.getMinValue(), singleTypeInfoCopy.getMinValue()); + assertEquals(singleTypeInfo.getMaxValue(), singleTypeInfoCopy.getMaxValue()); + assertEquals(singleTypeInfo.getCount(), singleTypeInfoCopy.getCount()); + assertEquals(singleTypeInfo.getDomain(), singleTypeInfoCopy.getDomain()); + assertEquals(singleTypeInfo.getUniqueCount(), singleTypeInfoCopy.getUniqueCount()); + assertEquals(singleTypeInfo.getPublicCount(), singleTypeInfoCopy.getPublicCount()); + + } + + @Test + public void testMerge() { + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId("url", "GET", 200, true, "param", SingleTypeInfo.EMAIL, 100, false); + CappedSet values1 = new CappedSet<>(); + values1.add("ankush"); + values1.add("avneesh"); + SingleTypeInfo singleTypeInfo1 = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 10,0,0,values1, SingleTypeInfo.Domain.ANY, 100, 1000); + singleTypeInfo1.setUniqueCount(100); + singleTypeInfo1.setPublicCount(200); + + CappedSet values2 = new CappedSet<>(); + values2.add("ankita"); + SingleTypeInfo singleTypeInfo2 = new SingleTypeInfo(paramId, new HashSet<>(), new HashSet<>(), 3,0,0,values2, SingleTypeInfo.Domain.ANY, 200, 2000); + singleTypeInfo2.setUniqueCount(100); + singleTypeInfo2.setPublicCount(200); + + singleTypeInfo1.merge(singleTypeInfo2); + + assertEquals(singleTypeInfo1.getCount(),13); + assertEquals(singleTypeInfo1.getValues().getElements().size(),3); + assertEquals(singleTypeInfo1.getMinValue(), 100); + assertEquals(singleTypeInfo1.getMaxValue(), 2000); + assertEquals(singleTypeInfo1.getUniqueCount(), 200); + assertEquals(singleTypeInfo1.getPublicCount(), 400); + } + + @Test + public void testSubTypeIsSensitive() { + boolean result = SingleTypeInfo.EMAIL.isSensitive(null); + assertTrue(result); + result = SingleTypeInfo.EMAIL.isSensitive(SingleTypeInfo.Position.RESPONSE_PAYLOAD); + assertTrue(result); + + result = SingleTypeInfo.GENERIC.isSensitive(null); + assertFalse(result); + result = SingleTypeInfo.GENERIC.isSensitive(SingleTypeInfo.Position.RESPONSE_PAYLOAD); + assertFalse(result); + + + result = SingleTypeInfo.JWT.isSensitive(SingleTypeInfo.Position.REQUEST_HEADER); + assertFalse(result); + result = SingleTypeInfo.EMAIL.isSensitive(SingleTypeInfo.Position.RESPONSE_PAYLOAD); + assertTrue(result); + + } + + @Test + public void testFindSubTypeFunction() { + SingleTypeInfo.Position position = SingleTypeInfo.findPosition(200, false); + assertEquals(SingleTypeInfo.Position.RESPONSE_PAYLOAD, position); + + position = SingleTypeInfo.findPosition(200,true); + assertEquals(SingleTypeInfo.Position.RESPONSE_HEADER, position); + + position = SingleTypeInfo.findPosition(-1, false); + assertEquals(SingleTypeInfo.Position.REQUEST_PAYLOAD, position); + + position = SingleTypeInfo.findPosition(-1,true); + assertEquals(SingleTypeInfo.Position.REQUEST_HEADER, position); + } + + @Test + public void testSetMinMaxValuesForNonNumbers() { + SingleTypeInfo singleTypeInfo = generateSTI(SingleTypeInfo.EMAIL); + assertEquals(SingleTypeInfo.ACCEPTED_MIN_VALUE, singleTypeInfo.getMaxValue()); + + singleTypeInfo.updateMinMaxValues(10000000); + singleTypeInfo.updateMinMaxValues(-1000000); + assertEquals(SingleTypeInfo.ACCEPTED_MIN_VALUE, singleTypeInfo.getMaxValue()); + assertEquals(SingleTypeInfo.ACCEPTED_MAX_VALUE, singleTypeInfo.getMinValue()); + } + + @Test + public void testSetMinMaxValuesForNumbers() { + SingleTypeInfo singleTypeInfo = generateSTI(SingleTypeInfo.INTEGER_32); + assertEquals(SingleTypeInfo.ACCEPTED_MIN_VALUE, singleTypeInfo.getMaxValue()); + assertEquals(SingleTypeInfo.ACCEPTED_MAX_VALUE, singleTypeInfo.getMinValue()); + + singleTypeInfo.updateMinMaxValues(Long.MAX_VALUE); + singleTypeInfo.updateMinMaxValues(Long.MIN_VALUE); + assertEquals(SingleTypeInfo.ACCEPTED_MAX_VALUE, singleTypeInfo.getMaxValue()); + assertEquals(SingleTypeInfo.ACCEPTED_MIN_VALUE, singleTypeInfo.getMinValue()); + + singleTypeInfo.updateMinMaxValues(10000000); + singleTypeInfo.updateMinMaxValues(-1000000); + assertEquals(SingleTypeInfo.ACCEPTED_MAX_VALUE, singleTypeInfo.getMaxValue()); + assertEquals(SingleTypeInfo.ACCEPTED_MIN_VALUE, singleTypeInfo.getMinValue()); + + singleTypeInfo = generateSTI(SingleTypeInfo.INTEGER_32); + + singleTypeInfo.updateMinMaxValues(10000000); + singleTypeInfo.updateMinMaxValues(-10000000); + assertEquals(10000000, singleTypeInfo.getMaxValue()); + assertEquals(-10000000, singleTypeInfo.getMinValue()); + + singleTypeInfo.updateMinMaxValues(20000000); + singleTypeInfo.updateMinMaxValues(-20000000); + assertEquals(20000000, singleTypeInfo.getMaxValue()); + assertEquals(-20000000, singleTypeInfo.getMinValue()); + + singleTypeInfo.updateMinMaxValues(20000); + singleTypeInfo.updateMinMaxValues(-2000); + assertEquals(20000000, singleTypeInfo.getMaxValue()); + assertEquals(-20000000, singleTypeInfo.getMinValue()); + } + + private SingleTypeInfo generateSTI(SingleTypeInfo.SubType subType) { + SingleTypeInfo.ParamId paramId = new SingleTypeInfo.ParamId( + "url", "GET", 200, true, "param", subType, 0, false + ); + return new SingleTypeInfo( + paramId, new HashSet<>(), new HashSet<>(), 100, Context.now(), 0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE + ); + } + + @Test + public void testFindLastKeyFromParam() { + String result = SingleTypeInfo.findLastKeyFromParam("name"); + assertEquals("name", result); + + result = SingleTypeInfo.findLastKeyFromParam("user#name"); + assertEquals("name", result); + + result = SingleTypeInfo.findLastKeyFromParam("cards#$#name"); + assertEquals("name", result); + + assertNull(SingleTypeInfo.findLastKeyFromParam(null)); + } + + + +} diff --git a/libs/dao/src/test/java/com/akto/dto/type/TestSubType.java b/libs/dao/src/test/java/com/akto/dto/type/TestSubType.java new file mode 100644 index 0000000000..28594f08fd --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/type/TestSubType.java @@ -0,0 +1,105 @@ +package com.akto.dto.type; + +import static org.junit.Assert.*; + +import java.util.HashMap; +import java.util.HashSet; + +import org.junit.Test; + +import com.akto.dto.AktoDataType; +import com.akto.dto.IgnoreData; +public class TestSubType { + + public void testInitializer(){ + SingleTypeInfo.aktoDataTypeMap = new HashMap<>(); + SingleTypeInfo.aktoDataTypeMap.put("JWT", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("PHONE_NUMBER", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("CREDIT_CARD", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + SingleTypeInfo.aktoDataTypeMap.put("IP_ADDRESS", new AktoDataType(null, false, null, 0, new IgnoreData(new HashMap<>(), new HashSet<>()))); + } + + @Test + public void testNumberInString() { + SingleTypeInfo.SubType happy = KeyTypes.findSubType("23423333333334", null,null); + assertEquals(SingleTypeInfo.INTEGER_64, happy); + } + + @Test + public void testJWT() { + testInitializer(); + SingleTypeInfo.SubType happy = KeyTypes.findSubType("eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g", "",null); + assertEquals(happy, SingleTypeInfo.JWT); + SingleTypeInfo.SubType changeHeader = KeyTypes.findSubType("eyJhbGciOiJSUzI1NiJ.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23g", "",null); + assertEquals(changeHeader, SingleTypeInfo.GENERIC); + SingleTypeInfo.SubType algMissingInHeader = KeyTypes.findSubType("eyJhbGRnIjogIlJTMjU2In0.eyJpc3MiOiJBa3RvIiwic3ViIjoicmVmcmVzaFRva2VuIiwic2lnbmVkVXAiOiJ0cnVlIiwidXNlcm5hbWUiOiJhbmtpdGFAZ21haWwuY29tIiwiaWF0IjoxNjM0OTcxMTMxLCJleHAiOjE2MzUwNTc1MzF9.Ph4Jv-fdggwvnbdVViD9BWUReYL0dVfVGuMRz4d2oZNnYzWV0JCmjpB68p6k0yyPPua_yagIWVZf_oYH9PUgS7EuaPYR-Vg6uxKR1HuXRA6wb8Xf4RPoFjJYkhWoYmv38V9Cz2My9U85wgGHGZXEufu8ubrFmIfOP6-A39M4meNGw48f5oOz8V337SX45uPc6jE0EfmM4l9EbqFFCF0lRXbMMzn-ijsyXxLkI5npWnqtW3PAHC2Rs3FV40tkRqHYF-WM6SzyHLBh6bVeyeOsFRBoEjv-zFh8yrYnT6OvCa6jII2A6uj4MQ2k11-5bDBhfVPVc4hEQz37H_DWwtf23dgdd", "",null); + assertEquals(algMissingInHeader, SingleTypeInfo.GENERIC); + SingleTypeInfo.SubType invalidLength = KeyTypes.findSubType("woiefjweofjweoifjweifjweiofjwiefjw", "",null); + assertEquals(invalidLength, SingleTypeInfo.GENERIC); + + } + + + @Test + public void testPhoneNumber() { + testInitializer(); + SingleTypeInfo.SubType happyIndian = KeyTypes.findSubType("+919967167961", "",null); + assertEquals(happyIndian, SingleTypeInfo.PHONE_NUMBER); + SingleTypeInfo.SubType wrongLength = KeyTypes.findSubType("+91996716796", "",null); + assertEquals(wrongLength, SingleTypeInfo.GENERIC); + SingleTypeInfo.SubType internation_spaces = KeyTypes.findSubType("+1 650 253 00 00", "",null); + assertEquals(internation_spaces, SingleTypeInfo.PHONE_NUMBER); + SingleTypeInfo.SubType international_dash = KeyTypes.findSubType("+1-541-754-3010", "",null); + assertEquals(international_dash, SingleTypeInfo.PHONE_NUMBER); + } + + + @Test + public void testCreditCard() { + testInitializer(); + SingleTypeInfo.SubType subType = KeyTypes.findSubType("378282246310005", "",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType(Long.valueOf("378282246310005"),"",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType("5267 318 1879 75449","",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType("5267 318 1879 75 4 49","",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType("5267-3181-8797-5449","",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType("4111 1111 1111 1111","",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType("5105 1051 0510 5100","",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType("5104 0600 0000 0008","",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType("4718 6091 0820 4366","",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType("5104 0155 5555 5558","",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType("5241 8100 0000 0000", "",null); + assertEquals(subType, SingleTypeInfo.CREDIT_CARD); + subType = KeyTypes.findSubType("3782822463100075", "",null); + assertEquals(subType, SingleTypeInfo.INTEGER_64); + subType = KeyTypes.findSubType("5241 8100 0000 A000", "",null); + assertEquals(subType, SingleTypeInfo.GENERIC); + } + + @Test + public void testIP() { + testInitializer(); + SingleTypeInfo.SubType happyIp4= KeyTypes.findSubType("172.8.9.28", "",null); + assertEquals(happyIp4, SingleTypeInfo.IP_ADDRESS); + SingleTypeInfo.SubType happyIp6= KeyTypes.findSubType("2001:0db8:85a3:0000:0000:8a2e:0370:7334", "",null); + assertEquals(happyIp6, SingleTypeInfo.IP_ADDRESS); + SingleTypeInfo.SubType octalIp4 = KeyTypes.findSubType("172.013.1.2", "",null); + assertEquals(octalIp4, SingleTypeInfo.GENERIC); + SingleTypeInfo.SubType negativeIp4 = KeyTypes.findSubType("172.8.-9.255", "",null); + assertEquals(negativeIp4, SingleTypeInfo.GENERIC); + SingleTypeInfo.SubType edgeCase = KeyTypes.findSubType("172.01.1.2","",null); + assertEquals(edgeCase, SingleTypeInfo.GENERIC); + SingleTypeInfo.SubType ipv4_mapped_ipv6= KeyTypes.findSubType("0000:0000:0000:0000:0000:ffff:192.168.100.228", "",null); + assertEquals(ipv4_mapped_ipv6, SingleTypeInfo.IP_ADDRESS); + } + +} diff --git a/libs/dao/src/test/java/com/akto/dto/type/TestUrlMethods.java b/libs/dao/src/test/java/com/akto/dto/type/TestUrlMethods.java new file mode 100644 index 0000000000..2d42039e9c --- /dev/null +++ b/libs/dao/src/test/java/com/akto/dto/type/TestUrlMethods.java @@ -0,0 +1,26 @@ +package com.akto.dto.type; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TestUrlMethods { + + @Test + public void testFromString() { + URLMethods.Method method = URLMethods.Method.fromString("Get"); + assertEquals(URLMethods.Method.GET, method); + + method = URLMethods.Method.fromString("gEt"); + assertEquals(URLMethods.Method.GET, method); + + method = URLMethods.Method.fromString("PATCH"); + assertEquals(URLMethods.Method.PATCH, method); + + method = URLMethods.Method.fromString("Avneesh"); + assertEquals(URLMethods.Method.OTHER, method); + + method = URLMethods.Method.fromString(null); + assertEquals(URLMethods.Method.OTHER, method); + } +} diff --git a/libs/dao/src/test/java/com/akto/types/TestCappedSet.java b/libs/dao/src/test/java/com/akto/types/TestCappedSet.java new file mode 100644 index 0000000000..da7b3c3220 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/types/TestCappedSet.java @@ -0,0 +1,27 @@ +package com.akto.types; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class TestCappedSet { + + @Test + public void testAdd() { + CappedSet values = new CappedSet<>(); + for (int i = 0; i< CappedSet.LIMIT +10; i++) { + values.add(i+""); + } + + assertEquals(CappedSet.LIMIT, values.elements.size()); + + for (int i = 0; i< CappedSet.LIMIT; i++) { + assertTrue(values.elements.contains(i+"")); + } + + for (int i = CappedSet.LIMIT; i< CappedSet.LIMIT +10; i++) { + assertFalse(values.elements.contains(i+"")); + } + + } +} diff --git a/libs/dao/src/test/java/com/akto/utils/MongoBasedTest.java b/libs/dao/src/test/java/com/akto/utils/MongoBasedTest.java new file mode 100644 index 0000000000..fceefb2b48 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/utils/MongoBasedTest.java @@ -0,0 +1,47 @@ +package com.akto.utils; + +import com.akto.DaoInit; +import com.akto.dao.context.Context; +import com.mongodb.ConnectionString; +import de.flapdoodle.embed.mongo.MongodExecutable; +import de.flapdoodle.embed.mongo.MongodProcess; +import de.flapdoodle.embed.mongo.MongodStarter; +import de.flapdoodle.embed.mongo.config.ImmutableMongodConfig; +import de.flapdoodle.embed.mongo.config.Net; +import de.flapdoodle.embed.mongo.distribution.Version; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +public class MongoBasedTest { + + public static final int ACCOUNT_ID = 12389; + + public static MongodExecutable mongodExe; + public static MongodProcess mongod; + + + @BeforeClass + public static void beforeClass() throws Exception { + MongodStarter starter = MongodStarter.getDefaultInstance(); + String bindIp = "localhost"; + ImmutableMongodConfig mongodConfig = ImmutableMongodConfig.builder() + .version(Version.Main.PRODUCTION) + .net(new Net(bindIp, 27019, false)) + .build(); + mongodExe = starter.prepare(mongodConfig); + mongod = mongodExe.start(); + DaoInit.init(new ConnectionString("mongodb://localhost:27019")); + Context.accountId.set(ACCOUNT_ID); + } + + @AfterClass + public static void afterClass() throws Exception { + if (mongod != null) { + mongod.stop(); + mongodExe.stop(); + } + } + + + +} diff --git a/libs/dao/src/test/java/com/akto/utils/TestJsonUtils.java b/libs/dao/src/test/java/com/akto/utils/TestJsonUtils.java new file mode 100644 index 0000000000..c1f0c7c0f3 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/utils/TestJsonUtils.java @@ -0,0 +1,92 @@ +package com.akto.utils; + +import com.akto.dto.type.RequestTemplate; +import com.akto.util.JSONUtils; +import com.akto.util.modifier.AddJkuJWTModifier; +import com.akto.util.modifier.ConvertToArrayPayloadModifier; +import com.akto.util.modifier.NestedObjectModifier; +import com.mongodb.BasicDBObject; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; + +public class TestJsonUtils { + + @Test + public void testModify() { + NestedObjectModifier nestedObjectModifier = new NestedObjectModifier(); + ConvertToArrayPayloadModifier convertToArrayPayloadModifier = new ConvertToArrayPayloadModifier(); + + String payload = "{'user': {'name': 'avneesh'}, 'friends': [{'name':'ankush'}, {'name':'ankita'}, {'name':'shivam'}, {'name':'ayush'}]}"; + Set s = new HashSet<>(); + s.add("friends#$#name"); + + String modifiedPayload = JSONUtils.modify(payload, s, convertToArrayPayloadModifier); + BasicDBObject modifiedBasicDbObject = RequestTemplate.parseRequestPayload(modifiedPayload, null); + Map> flattened = JSONUtils.flatten(modifiedBasicDbObject); + assertTrue(flattened.get("friends#$#name#$").contains("ankush")); + assertTrue(flattened.get("friends#$#name#$").contains("ankita")); + + modifiedPayload = JSONUtils.modify(payload, s, nestedObjectModifier); + modifiedBasicDbObject = RequestTemplate.parseRequestPayload(modifiedPayload, null); + flattened = JSONUtils.flatten(modifiedBasicDbObject); + assertTrue(flattened.get("friends#$#name#name").contains("ankush")); + assertTrue(flattened.get("friends#$#name#name").contains("ankita")); + + + s = new HashSet<>(); + s.add("json#$#name"); + + payload = "[{'name':'ankush'}, {'name':'ankita'}, {'name':'shivam'}, {'name':'ayush'}]"; + modifiedPayload = JSONUtils.modify(payload, s, convertToArrayPayloadModifier); + modifiedBasicDbObject = RequestTemplate.parseRequestPayload(modifiedPayload, null); + flattened = JSONUtils.flatten(modifiedBasicDbObject); + assertTrue(flattened.get("json#$#name#$").contains("ankush")); + assertTrue(flattened.get("json#$#name#$").contains("ankita")); + + modifiedPayload = JSONUtils.modify(payload, s, nestedObjectModifier); + modifiedBasicDbObject = RequestTemplate.parseRequestPayload(modifiedPayload, null); + flattened = JSONUtils.flatten(modifiedBasicDbObject); + assertTrue(flattened.get("json#$#name#name").contains("ankush")); + assertTrue(flattened.get("json#$#name#name").contains("ankita")); + } + + @Test + public void testNonJsonModify() { + String payload = "user=avneesh"; + Set s = new HashSet<>(); + s.add("friends#$#name"); + ConvertToArrayPayloadModifier convertToArrayPayloadModifier = new ConvertToArrayPayloadModifier(); + String modifiedPayload = JSONUtils.modify(payload, s, convertToArrayPayloadModifier); + assertNull(modifiedPayload); + } + + @Test + public void testModifyHeaderValues() { + Map> headers = new HashMap<>(); + headers.put("host", Collections.singletonList("akto.io")); + headers.put("fake", Collections.singletonList("a.b.c")); + + Map> result = JSONUtils.modifyHeaderValues(null, new AddJkuJWTModifier()); + assertNull(result); + + result = JSONUtils.modifyHeaderValues(new HashMap<>(), new AddJkuJWTModifier()); + assertNull(result); + + result = JSONUtils.modifyHeaderValues(headers, new AddJkuJWTModifier()); + assertNull(result); + + headers.put("token", Collections.singletonList("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ")); + headers.put("anotherToken", Collections.singletonList("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNTE2MjM5MDkwfQ.qnloZdeBgTtHUtvyhpo37TaxF1CZOLRI0rztKoITjm0NLJBpRqHB9mryP_Zy4FgV8MXDJb0-83pwjqOh8bdIWQKGukQaEL-dwetXCv9N8rWu3yud1ETtgvSNf5_k7X4X02ZbXeOW-qG39E60xtqvPySZ0zlaHbttZfpYus7SrklFoPFZFtDvzAXamFuLjZ1DIrgrW0i7BHl2CmwyEJS2IB3vpYdJzeN0ONlw7WYuVGxfG7BHeyUl7fI51dZHzYEBxvR4o0bZHxRqEWPQDwqAmc2Nbf_LA9p1muJakxT8DgNMxb3cbwusKpes9Ff7seBy1_tFke5HTViB4tO__XOeNw")); + + result = JSONUtils.modifyHeaderValues(headers, new AddJkuJWTModifier()); + assertNotNull(result); + assertEquals(headers.get("host"), result.get("host")); + assertEquals(headers.get("fake"), result.get("fake")); + assertNotEquals(headers.get("token"), result.get("token")); + assertNotEquals(headers.get("anotherToken"), result.get("anotherToken")); + + } +} diff --git a/libs/dao/src/test/java/com/akto/utils/modifier/TestAddJkuJwtModifier.java b/libs/dao/src/test/java/com/akto/utils/modifier/TestAddJkuJwtModifier.java new file mode 100644 index 0000000000..396e47aa46 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/utils/modifier/TestAddJkuJwtModifier.java @@ -0,0 +1,56 @@ +package com.akto.utils.modifier; + +import com.akto.dao.context.Context; +import com.akto.util.modifier.AddJkuJWTModifier; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import org.junit.Test; + +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +import static org.junit.Assert.*; + +public class TestAddJkuJwtModifier { + + @Test + public void testJwtModify() throws Exception { + AddJkuJWTModifier addJkuJWTModifier = new AddJkuJWTModifier(); + byte [] decoded = Base64.getDecoder().decode(addJkuJWTModifier.getPublicKey()); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded); + KeyFactory kf = KeyFactory.getInstance("RSA"); + PublicKey publicKey = kf.generatePublic(keySpec); + + // this jwt contains both iat and exp + String modifiedJWT = addJkuJWTModifier.jwtModify("token", "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwiZXhwIjoxNTE2MjM5MDkwfQ.qnloZdeBgTtHUtvyhpo37TaxF1CZOLRI0rztKoITjm0NLJBpRqHB9mryP_Zy4FgV8MXDJb0-83pwjqOh8bdIWQKGukQaEL-dwetXCv9N8rWu3yud1ETtgvSNf5_k7X4X02ZbXeOW-qG39E60xtqvPySZ0zlaHbttZfpYus7SrklFoPFZFtDvzAXamFuLjZ1DIrgrW0i7BHl2CmwyEJS2IB3vpYdJzeN0ONlw7WYuVGxfG7BHeyUl7fI51dZHzYEBxvR4o0bZHxRqEWPQDwqAmc2Nbf_LA9p1muJakxT8DgNMxb3cbwusKpes9Ff7seBy1_tFke5HTViB4tO__XOeNw"); + + // this will fail if invalid JWT (signature + time) + Jws jws = Jwts.parserBuilder() + .setSigningKey(publicKey) + .build() + .parseClaimsJws(modifiedJWT); + + assertEquals(AddJkuJWTModifier.JKU_VALUE , jws.getHeader().get("jku")); + long iat = Long.parseLong(jws.getBody().get("iat").toString()); + long exp = Long.parseLong(jws.getBody().get("exp").toString()); + assertTrue((Context.now() - iat) < 5); + assertEquals(68, (exp - iat)); // 68 is difference between original JWT's exp and iat + + // this jwt contains iat only + modifiedJWT = addJkuJWTModifier.jwtModify("token", "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ"); + + // this will fail if invalid JWT (signature + time) + jws = Jwts.parserBuilder() + .setSigningKey(publicKey) + .build() + .parseClaimsJws(modifiedJWT); + + assertEquals(AddJkuJWTModifier.JKU_VALUE , jws.getHeader().get("jku")); + iat = Long.parseLong(jws.getBody().get("iat").toString()); + assertTrue((Context.now() - iat) < 5); + assertNull(jws.getBody().get("exp")); + } +} diff --git a/libs/dao/src/test/java/com/akto/utils/modifier/TestInvalidSignatureJWTModifier.java b/libs/dao/src/test/java/com/akto/utils/modifier/TestInvalidSignatureJWTModifier.java new file mode 100644 index 0000000000..03254d3111 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/utils/modifier/TestInvalidSignatureJWTModifier.java @@ -0,0 +1,31 @@ +package com.akto.utils.modifier; + +import com.akto.util.modifier.InvalidSignatureJWTModifier; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class TestInvalidSignatureJWTModifier { + + @Test + public void testJwtModify() { + InvalidSignatureJWTModifier invalidSignatureJWTModifier = new InvalidSignatureJWTModifier(); + String jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ"; + String modifiedJWT = invalidSignatureJWTModifier.jwtModify("token", jwt); + + assertEquals( jwt.split("\\.")[0], modifiedJWT.split("\\.")[0]); + assertEquals( jwt.split("\\.")[1], modifiedJWT.split("\\.")[1]); + assertNotEquals( jwt.split("\\.")[2].split("")[0], modifiedJWT.split("\\.")[2].split("")[0]); + + + jwt = "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ"; + modifiedJWT = invalidSignatureJWTModifier.jwtModify("token", jwt); + + assertEquals("Bearer",modifiedJWT.split(" ")[0]); + + assertEquals( jwt.split(" ")[1].split("\\.")[0], modifiedJWT.split(" ")[1].split("\\.")[0]); + assertEquals( jwt.split(" ")[1].split("\\.")[1], modifiedJWT.split(" ")[1].split("\\.")[1]); + assertNotEquals( jwt.split(" ")[1].split("\\.")[2].split("")[0], modifiedJWT.split(" ")[1].split("\\.")[2].split("")[0]); + } +} diff --git a/libs/dao/src/test/java/com/akto/utils/modifier/TestNoneAlgoJWTModifier.java b/libs/dao/src/test/java/com/akto/utils/modifier/TestNoneAlgoJWTModifier.java new file mode 100644 index 0000000000..9ddd16bea9 --- /dev/null +++ b/libs/dao/src/test/java/com/akto/utils/modifier/TestNoneAlgoJWTModifier.java @@ -0,0 +1,22 @@ +package com.akto.utils.modifier; + +import com.akto.util.modifier.NoneAlgoJWTModifier; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TestNoneAlgoJWTModifier { + + @Test + public void testJWTModify() throws Exception { + NoneAlgoJWTModifier noneAlgoJWTModifier = new NoneAlgoJWTModifier(); + String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MiwiaWF0IjoxNTczMzU4Mzk2fQ.RwNNHvOKZk8p6fICIeezuajDalK8ZSOkEGMhZsRPFSk"; + String modifiedJWT = noneAlgoJWTModifier.jwtModify("token", jwt); + assertEquals("eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpZCI6MiwiaWF0IjoxNTczMzU4Mzk2fQ.", modifiedJWT); + + jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; + modifiedJWT = noneAlgoJWTModifier.jwtModify("token", jwt); + assertEquals("eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.", modifiedJWT); + + } +} diff --git a/libs/integrations/pom.xml b/libs/integrations/pom.xml new file mode 100644 index 0000000000..aa294f4ad7 --- /dev/null +++ b/libs/integrations/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + com.akto.libs + libs + ${revision} + + + com.akto.libs.integrations + integrations + jar + + + + + com.squareup.okhttp3 + okhttp + 4.9.3 + + + com.fasterxml.jackson.core + jackson-databind + 2.12.7.1 + compile + + + org.json + json + 20211205 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 8 + 8 + + + + src/main/java + src/test/java + + + \ No newline at end of file diff --git a/libs/integrations/src/main/java/com/akto/ApiRequest.java b/libs/integrations/src/main/java/com/akto/ApiRequest.java new file mode 100644 index 0000000000..a9c808018d --- /dev/null +++ b/libs/integrations/src/main/java/com/akto/ApiRequest.java @@ -0,0 +1,147 @@ +package com.akto; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.*; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class ApiRequest { + private static final ObjectMapper mapper = new ObjectMapper(); + private static final OkHttpClient client = new OkHttpClient(); +; + + public static JsonNode common(Request request) { + Call call = client.newCall(request); + Response response; + try { + response = call.execute(); + } catch (IOException e) { + ; + return null; + } + ResponseBody responseBody = response.body(); + if (responseBody == null) { + return null; + } + String body; + try { + body = responseBody.string(); + } catch (IOException e) { + ; + return null; + } + + try { + return mapper.readValue(body, JsonNode.class); + } catch (JsonProcessingException e) { + ; + return null; + } + } + + public static JsonNode processWithTimeout(Request request, TimeoutObject timeoutObj) throws Exception { + + OkHttpClient clientWithTimeout; + Call call; + + if (timeoutObj != null) { + clientWithTimeout = new OkHttpClient.Builder() + .connectTimeout(timeoutObj.getConnectTimeout(), TimeUnit.SECONDS) + .readTimeout(timeoutObj.getReadTimeout(), TimeUnit.SECONDS) + .writeTimeout(timeoutObj.getWriteTimeout(), TimeUnit.SECONDS) + .build(); + call = clientWithTimeout.newCall(request); + } else { + call = client.newCall(request); + } + + Response response; + try { + response = call.execute(); + } catch (IOException e) { + e.printStackTrace(); + throw new Exception(e.getMessage()); + } + ResponseBody responseBody = response.body(); + if (responseBody == null) { + return null; + } + String body; + try { + body = responseBody.string(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + try { + return mapper.readValue(body, JsonNode.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return null; + } + } + + public static JsonNode getRequest(Map headersMap, String url) { + Request.Builder builder = new Request.Builder().url(url); + for (String key: headersMap.keySet()) { + builder.addHeader(key, headersMap.get(key)); + } + + Request request = builder.build(); + return common(request); + + } + + public static JsonNode postRequest(Map headersMap, String url, String json) { + RequestBody body = RequestBody.create( json, MediaType.parse("application/json; charset=utf-8")); + + Request.Builder builder = new Request.Builder().post(body).url(url); + for (String key: headersMap.keySet()) { + builder.addHeader(key, headersMap.get(key)); + } + Request request = builder.build(); + + return common(request); + } + + public static JsonNode postRequestWithTimeout(Map headersMap, String url, String json, TimeoutObject timeoutObj) throws Exception { + RequestBody body = RequestBody.create( json, MediaType.parse("application/json; charset=utf-8")); + + Request.Builder builder = new Request.Builder().post(body).url(url); + for (String key: headersMap.keySet()) { + builder.addHeader(key, headersMap.get(key)); + } + Request request = builder.build(); + + return processWithTimeout(request, timeoutObj); + } + + public static JsonNode putRequest(Map headersMap, String url, String json) { + RequestBody body = RequestBody.create( json, MediaType.parse("application/json; charset=utf-8")); + + Request.Builder builder = new Request.Builder().put(body).url(url); + for (String key: headersMap.keySet()) { + builder.addHeader(key, headersMap.get(key)); + } + Request request = builder.build(); + + return common(request); + } + + public static JsonNode deleteRequest(Map headersMap, String url) { + Request.Builder builder = new Request.Builder().url(url).delete(); + for (String key: headersMap.keySet()) { + builder.addHeader(key, headersMap.get(key)); + } + + + Request request = builder.build(); + return common(request); + } + +} diff --git a/libs/integrations/src/main/java/com/akto/TimeoutObject.java b/libs/integrations/src/main/java/com/akto/TimeoutObject.java new file mode 100644 index 0000000000..710b45207e --- /dev/null +++ b/libs/integrations/src/main/java/com/akto/TimeoutObject.java @@ -0,0 +1,40 @@ +package com.akto; + +public class TimeoutObject { + private int connectTimeout; + private int readTimeout; + private int writeTimeout; + + public TimeoutObject(int connectTimeout, int readTimeout, int writeTimeout) { + this.connectTimeout = connectTimeout; + this.readTimeout = readTimeout; + this.writeTimeout = writeTimeout; + } + + public TimeoutObject() { } + + public int getConnectTimeout() { + return connectTimeout; + } + + public void setConnectTimeoutt(int connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public int getReadTimeout() { + return readTimeout; + } + + public void setReadTimeout(int readTimeout) { + this.readTimeout = readTimeout; + } + + public int getWriteTimeout() { + return writeTimeout; + } + + public void setWriteTimeout(int writeTimeout) { + this.writeTimeout = writeTimeout; + } + +} diff --git a/libs/integrations/src/main/java/com/akto/postman/Main.java b/libs/integrations/src/main/java/com/akto/postman/Main.java new file mode 100644 index 0000000000..3ad86bc9f5 --- /dev/null +++ b/libs/integrations/src/main/java/com/akto/postman/Main.java @@ -0,0 +1,211 @@ +package com.akto.postman; + +import com.akto.ApiRequest; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; + +import org.json.JSONObject; + +import java.util.*; + + +public class Main { + private final String apiKey; + public static final String BASE_URL = "https://api.getpostman.com/"; + + public Main(String apiKey) { + this.apiKey = apiKey; + } + + public void getPostmanCollection(String collectionId) { + String url = "https://api.getpostman.com/collections/" + ""; + JsonNode jsonNode = ApiRequest.getRequest(generateHeadersWithAuth(), url); + jsonNode.get("collection"); + + } + + public String createApi(String workspaceId, String apiName) { + String url = BASE_URL + "apis?workspace="+workspaceId; + + JSONObject child = new JSONObject(); + child.put("name", apiName); + child.put("summary", "Summary"); + child.put("description", "description"); + + JSONObject requestBody = new JSONObject(); + requestBody.put("api", child); + + String json = requestBody.toString(); + JsonNode node = ApiRequest.postRequest(generateHeadersWithAuth(), url,json); + String apiId = node.get("api").get("id").textValue(); + return apiId; + } + + public Map getVersion(String apiId, Set versionNameList) { + String url = BASE_URL + "apis/"+apiId+"/versions"; + JsonNode node = ApiRequest.getRequest(generateHeadersWithAuth(), url); + Iterator versionNodes = node.get("versions").elements(); + Map versionNameIdMap = new HashMap<>(); + for (String name: versionNameList) { + versionNameIdMap.put(name, null); + } + while (versionNodes.hasNext()) { + JsonNode n = versionNodes.next(); + String versionName = n.get("name").textValue(); + for (String v: versionNameList) { + if (versionName.equals(v)) { + versionNameIdMap.put(v, n.get("id").textValue()); + } + } + } + + for (String name: versionNameIdMap.keySet()) { + if (versionNameIdMap.get(name) == null) { + // create version + String vId = createVersion(name, apiId); + versionNameIdMap.put(name, vId); + } + } + + + return versionNameIdMap; + } + + public String createVersion(String versionName, String apiId) { + String url = BASE_URL + "apis/" + apiId + "/versions"; + + JSONObject child = new JSONObject(); + child.put("name", versionName); + JSONObject requestBody = new JSONObject(); + requestBody.put("version", child); + String json = requestBody.toString(); + + JsonNode jsonNode = ApiRequest.postRequest(generateHeadersWithAuth(),url,json); + + return jsonNode.get("version").get("id").textValue(); + } + + public void addSchema(String apiId, String version, String openApiSchema) { + String url1 = BASE_URL + "apis/" + apiId + "/versions/" + version; + JsonNode getNode = ApiRequest.getRequest(generateHeadersWithAuth(),url1); + Iterator versions = getNode.get("version").get("schema").elements(); + + JSONObject child = new JSONObject(); + child.put("language", "json"); + child.put("schema", openApiSchema); + child.put("type", "openapi3"); + JSONObject requestBody = new JSONObject(); + requestBody.put("schema", child); + String json = requestBody.toString(); + + // if version exists update it else create new one + if (versions.hasNext()) { + String url = BASE_URL + "apis/"+apiId+"/versions/" + version + "/schemas/" + versions.next().textValue(); + JsonNode node = ApiRequest.putRequest(generateHeadersWithAuth(), url,json); + } else { + String url = BASE_URL + "apis/"+apiId+"/versions/" + version + "/schemas"; + JsonNode node = ApiRequest.postRequest(generateHeadersWithAuth(), url,json); + } + + } + + public void createApiWithSchema(String workspaceId, String apiName, Map openApiSchemaMap) { + // Get akto_ API + String url = BASE_URL + "apis?name=" + apiName + "&" + "workspace=" + workspaceId; + JsonNode jsonNode = ApiRequest.getRequest(generateHeadersWithAuth(), url); + JsonNode apisNode = jsonNode.get("apis"); + String apiId; + + if (apisNode.elements().hasNext()) { + // get apiId + apiId = apisNode.get(0).get("id").textValue(); + } else { + // Create New API + apiId = createApi(workspaceId,apiName); + } + + // get versions (if not present create them) + Map apiVersionNameMap = getVersion(apiId,openApiSchemaMap.keySet()); + + + for (String name: apiVersionNameMap.keySet()) { + // Finally, replace schema for all versions + addSchema(apiId, apiVersionNameMap.get(name), openApiSchemaMap.get(name)); + } + + + + } + + + public JsonNode fetchApiCollections() { + String url = BASE_URL + "collections"; + JsonNode jsonNode = ApiRequest.getRequest(generateHeadersWithAuth(), url); + if (jsonNode == null) return null; + + return jsonNode.get("collections"); + } + + public JsonNode fetchPostmanCollectionString(String collectionId) { + String url = BASE_URL + "collections/" + collectionId; + JsonNode jsonNode = ApiRequest.getRequest(generateHeadersWithAuth(), url); + if (jsonNode == null) return null; + + return jsonNode; + } + + public JsonNode fetchWorkspaces() { + String url = BASE_URL + "workspaces"; + JsonNode jsonNode = ApiRequest.getRequest(generateHeadersWithAuth(), url); + if (jsonNode == null) return null; + + return jsonNode.get("workspaces"); + } + + public Map generateHeadersWithAuth() { + Map headersMap = new HashMap<>(); + headersMap.put("X-API-Key",apiKey); + return headersMap; + } + + public String createWorkspace() { + String url = BASE_URL + "workspaces"; + JSONObject json = new JSONObject(); + JSONObject workspace = new JSONObject(); + json.put("workspace", workspace); + + workspace.put("name", UUID.randomUUID().toString()); + workspace.put("type", "personal"); + workspace.put("description", "test"); + JsonNode jsonNode = ApiRequest.postRequest(generateHeadersWithAuth(), url, json.toString()); + return jsonNode.get("workspace").get("id").asText(); + } + + public String fetchOneApiFromWorkspace(String workspaceId) { + String url = BASE_URL + "apis?workspace=" + workspaceId; + JsonNode jsonNode = ApiRequest.getRequest(generateHeadersWithAuth(), url); + + ArrayNode apiList = (ArrayNode) jsonNode.get("apis"); + String apiId = apiList.get(0).get("id").asText(); + + url = BASE_URL + "apis/" + apiId; + return ApiRequest.getRequest(generateHeadersWithAuth(), url).toString(); + + } + + public String deleteWorkspace(String workspaceId) { + String url = BASE_URL + "workspaces/" + workspaceId; + JsonNode jsonNode = ApiRequest.deleteRequest(generateHeadersWithAuth(), url); + return jsonNode.toString(); + } + + public JsonNode fetchWorkspace(String workspaceId){ + String url = BASE_URL + "workspaces/" + workspaceId; + return ApiRequest.getRequest(generateHeadersWithAuth(), url); + } + + public JsonNode fetchCollection(String collectionId){ + String url = BASE_URL + "collections/" + collectionId; + return ApiRequest.getRequest(generateHeadersWithAuth(), url); + } +} diff --git a/libs/pom.xml b/libs/pom.xml new file mode 100644 index 0000000000..8bce86a189 --- /dev/null +++ b/libs/pom.xml @@ -0,0 +1,23 @@ + + + + 4.0.0 + + + com.akto + root + ${revision} + + + com.akto.libs + libs + pom + + + dao + utils + integrations + + + diff --git a/libs/utils/.gitignore b/libs/utils/.gitignore new file mode 100644 index 0000000000..c545dc0745 --- /dev/null +++ b/libs/utils/.gitignore @@ -0,0 +1,9 @@ +target/ +yarn.lock +.DS_Store +*.iml +*.pem +*.env +.settings +.project +.classpath \ No newline at end of file diff --git a/libs/utils/README.md b/libs/utils/README.md new file mode 100644 index 0000000000..ef0141a973 --- /dev/null +++ b/libs/utils/README.md @@ -0,0 +1,4 @@ +# utils + +compile using +`mvn install` diff --git a/libs/utils/pom.xml b/libs/utils/pom.xml new file mode 100644 index 0000000000..8fe9494816 --- /dev/null +++ b/libs/utils/pom.xml @@ -0,0 +1,145 @@ + + + 4.0.0 + + + com.akto.libs + libs + ${revision} + + + com.akto.libs.utils + utils + jar + + + + + junit + junit + 4.13.1 + test + + + com.itextpdf + itextpdf + 5.5.12 + + + org.apache.pdfbox + pdfbox + 2.0.24 + + + org.mongodb + mongodb-driver-sync + 4.2.1 + + + com.google.api-client + google-api-client + 1.23.0 + + + com.google.oauth-client + google-oauth-client-jetty + 1.23.0 + + + com.google.apis + google-api-services-sheets + v4-rev493-1.23.0 + + + com.google.apis + google-api-services-drive + v3-rev197-1.25.0 + + + org.slf4j + slf4j-simple + 1.7.5 + + + com.sendgrid + sendgrid-java + 4.4.1 + + + com.akto.libs.dao + dao + ${project.version} + + + org.apache.httpcomponents + httpclient + 4.5.13 + compile + + + com.fasterxml.jackson.core + jackson-databind + 2.12.7.1 + compile + + + de.sstoehr + har-reader + 2.2.0 + + + com.fasterxml.jackson.core + jackson-core + 2.12.2 + + + org.junit.jupiter + junit-jupiter-api + 5.4.2 + test + + + com.fasterxml.jackson.core + jackson-annotations + 2.12.2 + + + org.apache.kafka + kafka-clients + 3.0.0 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 8 + 8 + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + package + + test-jar + + + + + + + src/main/java + src/test/java + + + diff --git a/libs/utils/src/main/java/com/akto/InstanceDetails.java b/libs/utils/src/main/java/com/akto/InstanceDetails.java new file mode 100644 index 0000000000..99e78d4689 --- /dev/null +++ b/libs/utils/src/main/java/com/akto/InstanceDetails.java @@ -0,0 +1,33 @@ +package com.akto; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + + +public class InstanceDetails { + + public String instanceIp = null; + public static final InstanceDetails instance = new InstanceDetails(); + private static final HttpClient httpclient = HttpClients.createDefault(); + private InstanceDetails() {} + + public void fetchInstanceDetails() { + String url = "http://169.254.169.254/latest/meta-data/local-ipv4"; + HttpGet httpGet = new HttpGet(url); + + HttpResponse response = null; + try { + response = httpclient.execute(httpGet); + HttpEntity entity = response.getEntity(); + if (entity != null) { + instanceIp = EntityUtils.toString(entity); + } + } catch (Exception exception) { + } + } + +} diff --git a/libs/utils/src/main/java/com/akto/calendar/DateUtils.java b/libs/utils/src/main/java/com/akto/calendar/DateUtils.java new file mode 100644 index 0000000000..a246bbf344 --- /dev/null +++ b/libs/utils/src/main/java/com/akto/calendar/DateUtils.java @@ -0,0 +1,73 @@ +package com.akto.calendar; + +import com.akto.dao.context.Context; +import com.akto.util.DateUtils.TrackingPeriod; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +public class DateUtils { + + + public static int date(LocalDate of) { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd"); + return Integer.parseInt(dtf.format(of)); + } + + public static String prettifyDelta(int epochInSeconds) { + int diff = Context.now() - epochInSeconds; + if (diff < 120) { + return "1 minute ago"; + } else if (diff < 3600) { + return diff/60 + " minutes ago"; + } else if (diff < 7200) { + return "1 hour ago"; + } else if (diff < 86400) { + return diff/3600 + " hours ago"; + } + + return "couple of days ago"; + } + + public static LocalDate getStartLocalDate(TrackingPeriod period, int date) { + switch (period) { + case DAILY: return LocalDate.of(date/10000, (date/100)%100, date%100); + case WEEKLY: return LocalDate.of(date/10000, (date/100)%100, date%100).with(DayOfWeek.MONDAY); + case MONTHLY: return LocalDate.of(date/10000, (date/100)%100, 1); + case QUARTERLY: return LocalDate.of(date/10000, ((date/100)%100-1)/3 * 3+1, 1); + case HALF_YEARLY: LocalDate.of(date/10000, (date/100)%100 <= 6 ? 1 : 7, 1); + case YEARLY: LocalDate.of(date/10000, 1, 1); + default: + throw new IllegalArgumentException("Invalid Tracking period: " + period); + } + } + + public static int getStartDate(TrackingPeriod period, int date) { + return date(getStartLocalDate(period, date)); + } + + public static int getEndDate(TrackingPeriod period, int date) { + + LocalDate startDate = getStartLocalDate(period, date); + + switch (period) { + case DAILY: return date(startDate); + case WEEKLY: return date(startDate.plusDays(6)); + case MONTHLY: return date(startDate.plusMonths(1).minusDays(1)); + case QUARTERLY: return date(startDate.plusMonths(3).minusDays(1)); + case HALF_YEARLY: return date(startDate.plusMonths(6).minusDays(1)); + case YEARLY: return date(LocalDate.of(date/10000, 12, 31)); + default: + throw new IllegalArgumentException("Invalid Tracking period: " + period); + } + } + + public static long getNumberOfDaysLeftInPeriod(int startDate, int endDate) { + LocalDate localStartDate = LocalDate.parse(Integer.toString(startDate),DateTimeFormatter.ofPattern("yyyyMMdd")); + LocalDate localEndDate = LocalDate.parse(Integer.toString(endDate),DateTimeFormatter.ofPattern("yyyyMMdd")); + + return localStartDate.until(localEndDate, ChronoUnit.DAYS) + 1; + } +} diff --git a/libs/utils/src/main/java/com/akto/har/HAR.java b/libs/utils/src/main/java/com/akto/har/HAR.java new file mode 100644 index 0000000000..88f5fd287d --- /dev/null +++ b/libs/utils/src/main/java/com/akto/har/HAR.java @@ -0,0 +1,167 @@ +package com.akto.har; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.sstoehr.harreader.HarReader; +import de.sstoehr.harreader.HarReaderException; +import de.sstoehr.harreader.HarReaderMode; +import de.sstoehr.harreader.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.net.URLDecoder; +import java.util.*; + +public class HAR { + private final static ObjectMapper mapper = new ObjectMapper(); + private final List errors = new ArrayList<>(); + private static final Logger logger = LoggerFactory.getLogger(Har.class); + public static final String JSON_CONTENT_TYPE = "application/json"; + public static final String FORM_URL_ENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"; + public List getMessages(String harString, int collection_id) throws HarReaderException { + HarReader harReader = new HarReader(); + Har har = harReader.readFromString(harString, HarReaderMode.LAX); + + HarLog log = har.getLog(); + List entries = log.getEntries(); + + List entriesList = new ArrayList<>(); + int idx=0; + for (HarEntry entry: entries) { + idx += 1; + try { + Map result = getResultMap(entry); + if (result != null) { + result.put("akto_vxlan_id", collection_id+""); + entriesList.add(mapper.writeValueAsString(result)); + } + + } catch (Exception e) { + logger.error("Error while parsing har file on entry: " + idx + " ERROR: " + e); + errors.add("Error in entry " + idx); + } + } + + return entriesList; + } + + public static Map getResultMap(HarEntry entry) throws Exception { + HarRequest request = entry.getRequest(); + HarResponse response = entry.getResponse(); + Date dateTime = entry.getStartedDateTime(); + + List requestHarHeaders = request.getHeaders(); + List responseHarHeaders = response.getHeaders(); + + Map requestHeaderMap = convertHarHeadersToMap(requestHarHeaders); + Map responseHeaderMap = convertHarHeadersToMap(responseHarHeaders); + + String requestContentType = getContentType(requestHarHeaders); + + String requestPayload; + if (requestContentType == null) { + // get request data from querystring + Map paramMap = new HashMap<>(); + requestPayload = mapper.writeValueAsString(paramMap); + } else if (requestContentType.contains(JSON_CONTENT_TYPE)) { + String postData = request.getPostData().getText(); + if (postData == null) { + postData = "{}"; + } + + if (postData.startsWith("[")) { + requestPayload = postData; + } else { + Map paramMap = mapper.readValue(postData, new TypeReference>() {}); + requestPayload = mapper.writeValueAsString(paramMap); + } + } else if (requestContentType.contains(FORM_URL_ENCODED_CONTENT_TYPE)) { + String postText = request.getPostData().getText(); + if (postText == null) { + postText = ""; + } + + requestPayload = postText; + } else { + return null; + } + + + String akto_account_id = 1_000_000 + ""; + String path = getPath(request); + String requestHeaders = mapper.writeValueAsString(requestHeaderMap); + String responseHeaders = mapper.writeValueAsString(responseHeaderMap); + String method = request.getMethod().toString(); + String responsePayload = response.getContent().getText();; + String ip = "null"; + String time = (int) (dateTime.getTime() / 1000) + ""; + String statusCode = response.getStatus() + ""; + String type = request.getHttpVersion(); + String status = response.getStatusText(); + String contentType = getContentType(responseHarHeaders); + + Map result = new HashMap<>(); + result.put("akto_account_id",akto_account_id); + result.put("path",path); + result.put("requestHeaders",requestHeaders); + result.put("responseHeaders",responseHeaders); + result.put("method",method); + result.put("requestPayload",requestPayload); + result.put("responsePayload",responsePayload); + result.put("ip",ip); + result.put("time",time); + result.put("statusCode",statusCode); + result.put("type",type); + result.put("status",status); + result.put("contentType",contentType); + result.put("source", "HAR"); + + return result; + } + + public static boolean isApiRequest(List headers) { + String contentType = getContentType(headers); + if (contentType == null) { + return false; + } + return contentType.contains(JSON_CONTENT_TYPE); + } + + public static String getContentType(List headers) { + for (HarHeader harHeader: headers) { + if (harHeader.getName().equalsIgnoreCase("content-type")) { + return harHeader.getValue(); + } + } + return null; + } + + public static Map convertHarHeadersToMap(List headers) { + Map headerMap = new HashMap<>(); + + for (HarHeader harHeader: headers) { + if (harHeader != null) { + headerMap.put(harHeader.getName(), harHeader.getValue()); + } + } + + return headerMap; + } + + public static void addQueryStringToMap(List params, Map paramsMap) { + for (HarQueryParam param: params) { + paramsMap.put(param.getName(), param.getValue()); + } + } + + public static String getPath(HarRequest request) throws Exception { + String path = request.getUrl(); + if (path == null) throw new Exception("url path is null in har"); + return path; + } + + public List getErrors() { + return errors; + } +} diff --git a/libs/utils/src/main/java/com/akto/kafka/Kafka.java b/libs/utils/src/main/java/com/akto/kafka/Kafka.java new file mode 100644 index 0000000000..094071025d --- /dev/null +++ b/libs/utils/src/main/java/com/akto/kafka/Kafka.java @@ -0,0 +1,75 @@ +package com.akto.kafka; + +import com.akto.dao.context.Context; +import org.apache.kafka.clients.producer.*; +import org.apache.kafka.common.serialization.StringSerializer; + +import java.time.Duration; +import java.util.Properties; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class Kafka { + private KafkaProducer producer; + public boolean producerReady; + + public Kafka(String brokerIP, int lingerMS, int batchSize) { + producerReady = false; + try { + setProducer(brokerIP, lingerMS, batchSize); + } catch (Exception e) { + ; + } + } + + public void send(String message,String topic) { + if (!this.producerReady) return; + + ProducerRecord record = new ProducerRecord<>(topic,message); + producer.send(record, new DemoProducerCallback()); + } + + public void close() { + this.producerReady = false; + producer.close(Duration.ofMillis(0)); // close immediately + } + + private void setProducer(String brokerIP, int lingerMS, int batchSize) { + if (producer != null) close(); // close existing producer connection + + int requestTimeoutMs = 5000; + Properties kafkaProps = new Properties(); + kafkaProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, brokerIP); + kafkaProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + kafkaProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + kafkaProps.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize); + kafkaProps.put(ProducerConfig.LINGER_MS_CONFIG, lingerMS); + kafkaProps.put(ProducerConfig.RETRIES_CONFIG, 0); + kafkaProps.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, requestTimeoutMs); + kafkaProps.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, lingerMS + requestTimeoutMs); + producer = new KafkaProducer(kafkaProps); + + // test if connection successful by sending a test message in a blocking way + // calling .get() blocks the thread till we receive a message + // if any error then close the connection + ProducerRecord record = new ProducerRecord<>("akto.misc", "ping"); + try { + producer.send(record).get(); + producerReady = true; + } catch (Exception ignored) { + close(); + } + } + + private class DemoProducerCallback implements Callback { + @Override + public void onCompletion(RecordMetadata recordMetadata, Exception e) { + if (e != null) { + Kafka.this.close(); + } + } + } + +} + + diff --git a/libs/utils/src/main/java/com/akto/log/LoggerMaker.java b/libs/utils/src/main/java/com/akto/log/LoggerMaker.java new file mode 100644 index 0000000000..849d1be382 --- /dev/null +++ b/libs/utils/src/main/java/com/akto/log/LoggerMaker.java @@ -0,0 +1,37 @@ +package com.akto.log; + +import com.akto.dao.LogsDao; +import com.akto.dao.context.Context; +import com.akto.dto.Log; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoggerMaker { + + public final Logger logger; + private final Class aClass; + + public LoggerMaker(Class c) { + aClass = c; + logger = LoggerFactory.getLogger(c); + } + + public void errorAndAddToDb(String err) { + logger.error(err); + insert(err, "error"); + } + + public void infoAndAddToDb(String info) { + logger.info(info); + insert(info, "info"); + } + + private void insert(String info, String key) { + String text = aClass + " : " + info; + Log log = new Log(text, key, Context.now()); + LogsDao.instance.insertOne(log); + } + + + +} diff --git a/libs/utils/src/main/java/com/akto/notifications/slack/DailyUpdate.java b/libs/utils/src/main/java/com/akto/notifications/slack/DailyUpdate.java new file mode 100644 index 0000000000..49d10e8662 --- /dev/null +++ b/libs/utils/src/main/java/com/akto/notifications/slack/DailyUpdate.java @@ -0,0 +1,173 @@ +package com.akto.notifications.slack; + +import java.util.List; +import java.util.Map; + +import com.akto.dao.context.Context; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; + +public class DailyUpdate { + public static BasicDBObject createHeader(String title) { + BasicDBObject textObj = new BasicDBObject("type", "plain_text").append("text", title+"\n"); + return new BasicDBObject("type", "header").append("text", textObj); + } + + public static BasicDBObject createSimpleBlockText(String text) { + BasicDBObject textObj = new BasicDBObject("type", "mrkdwn").append("text",text+"\n"); + return new BasicDBObject("type", "section").append("text", textObj); + } + + + private static BasicDBObject createNumberSection(String title, int number, String link) { + BasicDBList fieldsList = new BasicDBList(); + BasicDBObject ret = new BasicDBObject("type", "section").append("fields", fieldsList); + + fieldsList.add(new BasicDBObject("type", "mrkdwn").append("text", "*"+title+"*\n<"+link+"|"+number+">")); + return ret; + } + + public static BasicDBObject createNumberSection(String title1, int number1, String link1, String title2, int number2, String link2) { + BasicDBList fieldsList = new BasicDBList(); + BasicDBObject ret = new BasicDBObject("type", "section").append("fields", fieldsList); + + BasicDBObject field1 = new BasicDBObject("type", "mrkdwn").append("text", "*"+title1+"*\n<"+link1+"|"+number1+">"); + BasicDBObject field2 = new BasicDBObject("type", "mrkdwn").append("text", "*"+title2+"*\n<"+link2+"|"+number2+">"); + + fieldsList.add(field1); + fieldsList.add(field2); + + return ret; + } + + public static class LinkWithDescription { + String header; + String link; + String description; + + public LinkWithDescription(String header, String link, String description) { + this.header = header; + this.link = link; + this.description = description; + } + } + + public static BasicDBList createLinksSection(List linkWithDescriptionList) { + BasicDBList ret = new BasicDBList(); + + int counter = 0; + for(LinkWithDescription linkWithDescription: linkWithDescriptionList) { + counter ++ ; + if (counter == 5) { + break; + } + String completeText = "><"+linkWithDescription.link+"|"+linkWithDescription.header+">"; + if (linkWithDescription.description != null) completeText += "\n>"+linkWithDescription.description; + ret.add(new BasicDBObject("type", "section").append("text", new BasicDBObject("type", "mrkdwn").append("text", completeText))); + } + + if (linkWithDescriptionList.size() > 4) { + String text = ("> and "+ (linkWithDescriptionList.size() - 4) +" more..."); + ret.add(new BasicDBObject("type", "section").append("text", new BasicDBObject("type", "mrkdwn").append("text", text))); + + } + + + return ret; + } + + public static BasicDBList createApiListSection(Map mapEndpointToSubtypes, String dashboardLink) { + BasicDBList ret = new BasicDBList(); + + int counter = 0; + for(String endpointData: mapEndpointToSubtypes.keySet()) { + counter ++ ; + if (counter == 5) { + break; + } + String link = dashboardLink + mapEndpointToSubtypes.get(endpointData); + String text = "<"+link+"|"+endpointData+">"; + ret.add(new BasicDBObject("type", "section").append("text", new BasicDBObject("type", "mrkdwn").append("text", ">" + text))); + } + + if (mapEndpointToSubtypes.size() > 4) { + String text = ("> and "+ (mapEndpointToSubtypes.size() - 4) +" more..."); + ret.add(new BasicDBObject("type", "section").append("text", new BasicDBObject("type", "mrkdwn").append("text", text))); + + } + + + return ret; + } + + + private int totalSensitiveEndpoints; + private int totalEndpoints; + private int newSensitiveEndpoints; + private int newEndpoints; + private int newSensitiveParams; + private int newParams; + private Map mapEndpointToSubtypes; + private String dashboardLink; + private int startTimestamp; + private int endTimestamp; + + public DailyUpdate( + int totalSensitiveEndpoints, int totalEndpoints, + int newSensitiveEndpoints, int newEndpoints, + int newSensitiveParams, int newParams, + int startTimestamp,int endTimestamp, + Map mapEndpointToSubtypes, String dashboardLink + ) { + this.totalSensitiveEndpoints = totalSensitiveEndpoints; + this.totalEndpoints = totalEndpoints; + this.newSensitiveEndpoints = newSensitiveEndpoints; + this.newEndpoints = newEndpoints; + this.newSensitiveParams = newSensitiveParams; + this.newParams=newParams; + this.startTimestamp=startTimestamp; + this.endTimestamp=endTimestamp; + this.mapEndpointToSubtypes = mapEndpointToSubtypes; + this.dashboardLink = dashboardLink; + } + + + public String toJSON() { + BasicDBList sectionsList = new BasicDBList(); + BasicDBObject ret = new BasicDBObject("blocks", sectionsList); + + sectionsList.add(createHeader("API Inventory Summary For Today :ledger: :")); + // sectionsList.add(createNumberSection("Total Sensitive Endpoints", totalSensitiveEndpoints, "Total Endpoints", totalEndpoints)); + + // int end = Context.now(); + // int start = end - 24 * 60 * 60; + + String linkNewEndpoints = dashboardLink + "/dashboard/observe/changes?tab=endpoints&start="+startTimestamp+"&end="+endTimestamp; + BasicDBObject topNumberSection = createNumberSection( + "New Sensitive Endpoints", + newSensitiveEndpoints, + linkNewEndpoints, + "New Endpoints", + newEndpoints, + linkNewEndpoints + ); + sectionsList.add(topNumberSection); + + String linkSensitiveParams = dashboardLink + "/dashboard/observe/changes?tab=parameters&start="+startTimestamp+"&end="+endTimestamp; + BasicDBObject bottomNumberSection = createNumberSection( + "New Sensitive Parameters", + newSensitiveParams, + linkSensitiveParams, + "New Parameters", + newParams, + linkSensitiveParams + ); + sectionsList.add(bottomNumberSection); + + if (mapEndpointToSubtypes.size() > 0) { + sectionsList.addAll(createApiListSection(mapEndpointToSubtypes, dashboardLink)); + } + + return ret.toJson(); + } +} diff --git a/libs/utils/src/main/java/com/akto/notifications/slack/TestSummaryGenerator.java b/libs/utils/src/main/java/com/akto/notifications/slack/TestSummaryGenerator.java new file mode 100644 index 0000000000..4e987b09ed --- /dev/null +++ b/libs/utils/src/main/java/com/akto/notifications/slack/TestSummaryGenerator.java @@ -0,0 +1,242 @@ +package com.akto.notifications.slack; + +import com.akto.DaoInit; +import com.akto.calendar.DateUtils; +import com.akto.dao.context.Context; +import com.akto.dao.testing.TestingRunDao; +import com.akto.dao.testing.TestingRunResultSummariesDao; +import com.akto.dao.testing.WorkflowTestResultsDao; +import com.akto.dao.testing.WorkflowTestsDao; +import com.akto.dao.testing_run_findings.TestingRunIssuesDao; +import com.akto.dto.ApiInfo; +import com.akto.dto.OriginalHttpRequest; +import com.akto.dto.test_run_findings.TestingIssuesId; +import com.akto.dto.test_run_findings.TestingRunIssues; +import com.akto.dto.testing.*; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.ConnectionString; +import com.mongodb.client.model.Filters; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; + +import java.util.*; + +import static com.akto.notifications.slack.DailyUpdate.*; + +public class TestSummaryGenerator { + + private int accountId; + + + public TestSummaryGenerator(int accountId) { + this.accountId = accountId; + } + + public String toJson(String dashboardLink) { + + GenerateResult generateResult = generate(Context.now() - 24*60*60); + + BasicDBList sectionsList = new BasicDBList(); + BasicDBObject ret = new BasicDBObject("blocks", sectionsList); + + sectionsList.add(createHeader("API Testing Summary For Today :testing: :")); + + String mainTestingLink = dashboardLink + "/dashboard/testing/active"; + + int totalApis = 0; + for (TestingRunResultSummary summary: generateResult.automatedTestingResultSummary) { + totalApis += summary.getTotalApis(); + } + + BasicDBObject automatedTestStatsSection= createNumberSection( + "Automated test runs", + generateResult.automatedTestingResultSummary.size(), + mainTestingLink, + "Total APIs tested", + totalApis, + mainTestingLink + ); + sectionsList.add(automatedTestStatsSection); + + List linkWithDescriptionList = new ArrayList<>(); + for (TestingRunIssues issue: generateResult.testingRunIssues) { + TestingIssuesId id = issue.getId(); + String issueName = id.getTestSubCategory() == null ? id.getTestCategoryFromSourceConfig() : id.getTestSubCategory().getIssueDescription(); + ApiInfo.ApiInfoKey apiInfoKey = id.getApiInfoKey(); + String url = apiInfoKey.getUrl(); + String method = apiInfoKey.getMethod().name(); + linkWithDescriptionList.add(new LinkWithDescription(method + " " + url, dashboardLink+"/dashboard/issues", issueName)); + } + + if (!linkWithDescriptionList.isEmpty()) { + sectionsList.add(createSimpleBlockText("*Issues found:* \n")); + sectionsList.addAll(createLinksSection(linkWithDescriptionList)); + } else { + sectionsList.add(createSimpleBlockText("_No issues found!!_:partying_face:")); + } + + if (generateResult.workflowTestResults.isEmpty()) { + return ret.toJson(); + } + + int workflowAPIs = 0; + for (WorkflowTestResult workflowTestResult: generateResult.workflowTestResults) { + workflowAPIs += workflowTestResult.getNodeResultMap().size(); + } + + BasicDBObject workflowTestStatsSection= createNumberSection( + "Workflow test runs", + generateResult.workflowTestResults.size(), + mainTestingLink, + "Total API requests made", + workflowAPIs, + mainTestingLink + ); + sectionsList.add(workflowTestStatsSection); + + List vulnerableWorkflowTestResults = new ArrayList<>(); + List errorWorkflowTestResults = new ArrayList<>(); + for (WorkflowTestResult workflowTestResult: generateResult.workflowTestResults) { + Map nodeResultMap = workflowTestResult.getNodeResultMap(); + String vulnerable = null; + String error = null; + for (String key: nodeResultMap.keySet()) { + WorkflowTestResult.NodeResult nodeResult = nodeResultMap.get(key); + if (nodeResult.isVulnerable()) { + vulnerable = "Validation failed in: *" + key + "* node"; + } + + if (!nodeResult.getErrors().isEmpty()) { + error = "_" + nodeResult.getErrors().get(0) + "_" + " in *" + key + "* node"; + } + } + + TestingRunResultSummary testingRunResultSummary = generateResult.testingRunResultSummaryMap.get(workflowTestResult.getTestingRunResultSummaryId()); + int duration = testingRunResultSummary.getEndTimestamp() - testingRunResultSummary.getStartTimestamp(); + String lastRunString = DateUtils.prettifyDelta(testingRunResultSummary.getStartTimestamp()); + + String commonDescription = "Test started " + lastRunString + " and ran for " + duration + " seconds. "; + LinkWithDescription linkWithDescription = new LinkWithDescription( + workflowTestResult.getWorkflowTestId()+"", + dashboardLink + "/dashboard/testing/" + workflowTestResult.getTestRunId() +"/results" , + null + ); + if (vulnerable != null) { + linkWithDescription.description = commonDescription + vulnerable; + vulnerableWorkflowTestResults.add(linkWithDescription); + } + + if (error != null) { + linkWithDescription.description = commonDescription + error; + errorWorkflowTestResults.add(linkWithDescription); + } + } + + if (!vulnerableWorkflowTestResults.isEmpty()) { + sectionsList.add(createSimpleBlockText("*Vulnerable workflow tests:* \n")); + sectionsList.addAll(createLinksSection(vulnerableWorkflowTestResults)); + } else { + sectionsList.add(createSimpleBlockText("_No vulnerable workflow tests_:partying_face:")); + } + + if (!errorWorkflowTestResults.isEmpty()) { + sectionsList.add(createSimpleBlockText("*Error workflows tests:* \n")); + sectionsList.addAll(createLinksSection(errorWorkflowTestResults)); + } else { + sectionsList.add(createSimpleBlockText("_No error in workflow tests_:partying_face:")); + } + + return ret.toJson(); + } + + + public GenerateResult generate(int startTs) { + Context.accountId.set(this.accountId); + + Bson testingRunBsonFilter = Filters.gte(TestingRunResultSummary.START_TIMESTAMP, startTs); + List testingRunResultSummaryList = TestingRunResultSummariesDao.instance.findAll(testingRunBsonFilter); + + List automatedTestingResultSummary = new ArrayList<>(); + List workflowTestResults = new ArrayList<>(); + + List workflowTestingResultSummaryIds = new ArrayList<>(); + + Map testingRunMap = new HashMap<>(); + Map testingRunResultSummaryMap = new HashMap<>(); + Set automatedTestingRunSet = new HashSet<>(); + Set workflowTestingRunSet = new HashSet<>(); + + for (TestingRunResultSummary summary: testingRunResultSummaryList) { + testingRunMap.put(summary.getTestingRunId(), null); + testingRunResultSummaryMap.put(summary.getId(), summary); + } + + List testingRunList = TestingRunDao.instance.findAll(Filters.in("_id", testingRunMap.keySet())); + for (TestingRun testingRun: testingRunList) { + testingRunMap.put(testingRun.getId(), testingRun); + if (testingRun.getTestIdConfig() == 1) { + workflowTestingRunSet.add(testingRun.getId()); + } else { + automatedTestingRunSet.add(testingRun.getId()); + } + } + + for (TestingRunResultSummary summary: testingRunResultSummaryList) { + if (automatedTestingRunSet.contains(summary.getTestingRunId())) { + automatedTestingResultSummary.add(summary); + } else if (workflowTestingRunSet.contains(summary.getTestingRunId())) { + workflowTestingResultSummaryIds.add(summary.getId()); + } + } + + workflowTestResults = WorkflowTestResultsDao.instance.findAll(Filters.in(WorkflowTestResult.TESTING_RUN_RESULT_SUMMARY_ID, workflowTestingResultSummaryIds)); + Set workflowIds = new HashSet<>(); + for (WorkflowTestResult workflowTestResult: workflowTestResults) { + workflowIds.add(workflowTestResult.getWorkflowTestId()); + } + List workflowTests = WorkflowTestsDao.instance.findAll(Filters.in("_id", workflowIds)); + Map workflowTestsMap = new HashMap<>(); + for (WorkflowTest workflowTest :workflowTests) { + workflowTestsMap.put(workflowTest.getId(), workflowTest); + } + + List testingRunIssues = TestingRunIssuesDao.instance.findAll(Filters.gte(TestingRunIssues.LAST_SEEN, startTs)); + + return new GenerateResult(workflowTestResults, automatedTestingResultSummary, testingRunMap, + testingRunResultSummaryMap, testingRunIssues, workflowTestsMap); + } + + + + private static class GenerateResult { + List workflowTestResults; + List automatedTestingResultSummary; + Map testingRunMap; + Map testingRunResultSummaryMap; + List testingRunIssues; + Map workflowTestsMap; + + public GenerateResult(List workflowTestResults, + List automatedTestingResultSummary, + Map testingRunMap, + Map testingRunResultSummaryMap, + List testingRunIssues, + Map workflowTestsMap) { + this.workflowTestResults = workflowTestResults; + this.automatedTestingResultSummary = automatedTestingResultSummary; + this.testingRunMap = testingRunMap; + this.testingRunResultSummaryMap = testingRunResultSummaryMap; + this.testingRunIssues = testingRunIssues; + this.workflowTestsMap = workflowTestsMap; + } + } + + public int getAccountId() { + return accountId; + } + + public void setAccountId(int accountId) { + this.accountId = accountId; + } +} diff --git a/libs/utils/src/main/java/com/akto/open_api/Main.java b/libs/utils/src/main/java/com/akto/open_api/Main.java new file mode 100644 index 0000000000..c26025923f --- /dev/null +++ b/libs/utils/src/main/java/com/akto/open_api/Main.java @@ -0,0 +1,181 @@ +package com.akto.open_api; + +import com.akto.dao.ApiCollectionsDao; +import com.akto.dao.SingleTypeInfoDao; +import com.akto.dto.ApiCollection; +import com.akto.dto.type.SingleTypeInfo; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; +import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; +import com.mongodb.client.model.Filters; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.servers.Server; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; + +public class Main { + private static final Logger logger = LoggerFactory.getLogger(Main.class); + private static final ObjectMapper mapper = new ObjectMapper(); + + + public static OpenAPI init(String info,Map>>> stiList, boolean includeHeaders, String serverUrl) throws Exception { + OpenAPI openAPI = new OpenAPI(); + addPaths(openAPI, stiList, includeHeaders); + if (serverUrl !=null && !serverUrl.startsWith("http")) serverUrl = "https://" + serverUrl; + addServer(serverUrl, openAPI); + Paths paths = PathBuilder.parameterizePath(openAPI.getPaths()); + openAPI.setPaths(paths); + addInfo(openAPI,info); + return openAPI; + } + + + public static void addPaths(OpenAPI openAPI, Map>>> stiList, boolean includeHeaders) { + Paths paths = new Paths(); + for(String url : stiList.keySet()){ + if (url.endsWith(".js") || url.endsWith(".css") || url.endsWith(".svg") || url.endsWith(".jpg") || url.endsWith(".png")) { + continue; + } + buildPathsFromSingleTypeInfosPerUrl(stiList.get(url), url,paths, includeHeaders); + } + openAPI.setPaths(paths); + } + + public static void buildPathsFromSingleTypeInfosPerUrl(Map>> stiMap, String url, Paths paths, boolean includeHeaders) { + for (String method: stiMap.keySet()) { + Map> responseWiseMap = stiMap.get(method); + for (Integer responseCode: responseWiseMap.keySet()) { + List singleTypeInfoList = responseWiseMap.get(responseCode); + try { + addPathItems(responseCode, paths, url, method, singleTypeInfoList, includeHeaders); + } catch (Exception e) { + logger.error("ERROR in buildPathsFromSingleTypeInfosPerUrl " + e); + } + } + } + } + + public static void addPathItems(int responseCode, Paths paths, String url, String method, List singleTypeInfoList, boolean includeHeaders) throws Exception { + Schema schema = null; + try { + schema = buildSchema(singleTypeInfoList); + } catch (Exception e) { + logger.error("ERROR in building schema in addPathItems " + e); + } + if (schema == null) { + schema = new ObjectSchema(); + schema.setDescription("AKTO_ERROR while building schema"); + } + List headerParameters = new ArrayList<>(); + try{ + headerParameters = buildHeaders(singleTypeInfoList); + } catch (Exception e) { + logger.error("ERROR in building headers in addPathItems " + e); + } + + PathBuilder.addPathItem(paths, url, method, responseCode, schema, headerParameters, includeHeaders); + } + + public static List buildHeaders(List singleTypeInfoList) throws Exception{ + List headerParameters = new ArrayList<>(); + ObjectSchema schema =new ObjectSchema(); + for (SingleTypeInfo singleTypeInfo: singleTypeInfoList) { + if(singleTypeInfo.isIsHeader()){ + List cc = SchemaBuilder.getCustomSchemasFromSingleTypeInfo(singleTypeInfo); + SchemaBuilder.build(schema, cc); + } + } + if (schema.getProperties() == null) return headerParameters; + for(String header:schema.getProperties().keySet()){ + Parameter head = new Parameter(); + head.setName(header); + head.setIn("header"); + head.setSchema(schema.getProperties().get(header)); + headerParameters.add(head); + } + return headerParameters; + } + + public static Schema buildSchema(List singleTypeInfoList) throws Exception { + ObjectSchema schema =new ObjectSchema(); + for (SingleTypeInfo singleTypeInfo: singleTypeInfoList) { + if(singleTypeInfo.isIsHeader()){ + continue; + } + List cc = SchemaBuilder.getCustomSchemasFromSingleTypeInfo(singleTypeInfo); + SchemaBuilder.build(schema, cc); + } + schema.setDescription("Sample description"); + return schema; + } + + + public static void addServer(String url, OpenAPI openAPI) { + if (url != null) { + Server server = new Server(); + server.setUrl(url); + openAPI.setServers(Collections.singletonList(server)); + } + + Paths paths = openAPI.getPaths(); + Paths newPaths = new Paths(); + for (String path: paths.keySet()) { + PathItem pathItem = paths.get(path); + if (path.startsWith("http")) { + try { + URI uri = new URI(path); + String pathUrl = uri.getScheme() + "://"+uri.getAuthority(); + Server pathServer = new Server(); + pathServer.setUrl(pathUrl); + String newPath = uri.getPath(); + newPaths.put(newPath, pathItem); + pathItem.setServers(Collections.singletonList(pathServer)); + } catch (URISyntaxException e) { + ; + } + } else { + newPaths.put(path, pathItem); + } + + } + + openAPI.setPaths(newPaths); + + + } + + + public static String convertOpenApiToJSON(OpenAPI openAPI) throws Exception { + mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector(){ + @Override + public boolean hasIgnoreMarker(final AnnotatedMember m) { + List exclusions = Collections.singletonList("exampleSetFlag"); + return exclusions.contains(m.getName())|| super.hasIgnoreMarker(m); + } + }); + + mapper.setSerializationInclusion(NON_NULL); + return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(openAPI); + } + + public static void addInfo(OpenAPI openAPI, String collectionName) { + Info info = new Info(); + info.setDescription("Akto generated openAPI file"); + info.setTitle(collectionName); + info.setVersion("1.0.0"); + openAPI.setInfo(info); + } +} diff --git a/libs/utils/src/main/java/com/akto/open_api/PathBuilder.java b/libs/utils/src/main/java/com/akto/open_api/PathBuilder.java new file mode 100644 index 0000000000..12d6ce9cae --- /dev/null +++ b/libs/utils/src/main/java/com/akto/open_api/PathBuilder.java @@ -0,0 +1,179 @@ +package com.akto.open_api; + +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.headers.Header; +import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PathBuilder { + + public static void addPathItem(Paths paths, String url, String method , int responseCode, Schema schema,List headerParameters, boolean includeHeaders) throws Exception { + PathItem pathItem = paths.getOrDefault(url, new PathItem()); + pathItem.setDescription("description"); + Operation operation = getOperation(pathItem,method); + if (operation == null) { + operation = new Operation(); + operation.setOperationId(generateOperationId(url, method)); + operation.setSummary(generateSummary(url, method)); + } + + if (responseCode == -1) { + RequestBody requestBody = new RequestBody(); + Content requestBodyContent = requestBody.getContent(); + if (requestBodyContent == null) requestBodyContent = new Content(); + + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + requestBodyContent.put("application/json", mediaType); + + requestBody.setContent(requestBodyContent); + operation.setRequestBody(requestBody); + if (includeHeaders) { + operation.setParameters(headerParameters); + } + setOperation(pathItem, method, operation); + paths.addPathItem(url, pathItem); + return ; + } + + ApiResponses apiResponses = operation.getResponses(); + if (apiResponses == null) apiResponses = new ApiResponses(); + + ApiResponse apiResponse = new ApiResponse(); + Content content = new Content(); + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + content.put("application/json", mediaType); + apiResponse.setContent(content); + apiResponse.setDescription("description"); + if (includeHeaders) { + Map headers = paramListToHeader(headerParameters); + apiResponse.setHeaders(headers); + } + apiResponses.put(responseCode+"", apiResponse); + + operation.setResponses(apiResponses); + setOperation(pathItem, method, operation); + paths.addPathItem(url, pathItem); + } + + public static Map paramListToHeader(List headerParameters){ + Map headers = new HashMap<>(); + for(Parameter parameter: headerParameters){ + if(!headers.containsKey(parameter.getName())){ + Header head = new Header(); + head.setSchema(parameter.getSchema()); + headers.put(parameter.getName(),head); + } + } + return headers; + } + + public static Paths parameterizePath(Paths paths) { + Paths newPaths = new Paths(); + for (String url: paths.keySet()) { + PathItem pathItem = paths.get(url); + List parameters = new ArrayList<>(); + String[] urlList = url.split("/"); + int idx = 0; + for (int i=0;i< urlList.length; i++) { + String u = urlList[i]; + if (u.equals("INTEGER") || u.equals("STRING")) { + idx += 1; + String paramName = "{param" + idx + "}"; + urlList[i] = paramName; + Parameter parameter = new Parameter(); + parameter.setIn("path"); + parameter.setName("param"+idx); + if (u.equals("INTEGER")) { + parameter.setSchema(new IntegerSchema()); + } else { + parameter.setSchema(new StringSchema()); + } + parameters.add(parameter); + } + } + String newUrl = String.join( "/",urlList); + if (!newUrl.startsWith("/")) { + newUrl = "/" + newUrl; + } + + + pathItem.setParameters(parameters); + newPaths.put(newUrl, pathItem); + } + return newPaths; + } + + public static String generateOperationId(String url, String method) { + return url + "-" + method; + } + + public static String generateSummary(String url, String method) { + return method + " request for endpoint " + url; + } + + public static void setOperation(PathItem pathItem, String method, Operation operation) throws Exception { + switch (method.toLowerCase()) { + case "get": + pathItem.setGet(operation); + return ; + case "post": + pathItem.setPost(operation); + return ; + case "delete": + pathItem.setDelete(operation); + return ; + case "put": + pathItem.setPut(operation); + return ; + case "patch": + pathItem.setPatch(operation); + return ; + case "head": + pathItem.setHead(operation); + return ; + case "options": + pathItem.setOptions(operation); + return ; + case "trace": + pathItem.setTrace(operation); + return ; + default: + throw new Exception("invalid operation"); + } + } + + public static Operation getOperation(PathItem pathItem, String method) throws Exception { + switch (method.toLowerCase()) { + case "get": + return pathItem.getGet(); + case "post": + return pathItem.getPost(); + case "delete": + return pathItem.getDelete(); + case "put": + return pathItem.getPut(); + case "patch": + return pathItem.getPatch(); + case "head": + return pathItem.getHead(); + case "options": + return pathItem.getOptions(); + case "trace": + return pathItem.getTrace(); + default: + throw new Exception("invalid operation"); + } + } +} diff --git a/libs/utils/src/main/java/com/akto/open_api/SchemaBuilder.java b/libs/utils/src/main/java/com/akto/open_api/SchemaBuilder.java new file mode 100644 index 0000000000..02696dcc95 --- /dev/null +++ b/libs/utils/src/main/java/com/akto/open_api/SchemaBuilder.java @@ -0,0 +1,134 @@ +package com.akto.open_api; + +import com.akto.dto.type.SingleTypeInfo; +import com.akto.types.CappedSet; + +import io.swagger.v3.oas.models.media.*; + +import java.util.*; + +public class SchemaBuilder { + public static Schema build(Schema schema, List params) throws Exception { + Map properties = schema.getProperties(); + if (properties == null) { + properties = new HashMap<>(); + } + CustomSchema curr = params.remove(0); + Schema schema1 = getSchema(curr); + + + if (params.size() == 0) { + schema1.setExample(curr.example); + properties.put(curr.name, schema1); + if (schema instanceof ObjectSchema) { + schema.setProperties(properties); + return schema; + } else if (schema instanceof ArraySchema) { + ArraySchema arraySchema = (ArraySchema) schema; + arraySchema.setItems(schema1); + return arraySchema; + } + throw new Exception(); + } + + Schema child; + if (schema instanceof ObjectSchema) { + Schema next = properties.get(curr.name); + if (next == null) { + next = schema1; + } + child = build(next, params); + } else if (schema instanceof ArraySchema) { + ArraySchema arraySchema = (ArraySchema) schema; + Schema next = arraySchema.getItems(); + if (next == null) { + next = schema1; + } + child = build(next, params); + } else { + throw new Exception("Schema instance of : " + schema.getClass()); + } + + if (schema instanceof ObjectSchema) { + properties.put(curr.name,child); + schema.setProperties(properties); + return schema; + } else if (schema instanceof ArraySchema) { + ArraySchema arraySchema = (ArraySchema) schema; + arraySchema.setItems(child); + return arraySchema; + } + throw new Exception(); + + } + + public static Schema getSchema(CustomSchema customSchema) throws Exception { + Class schemaClass = customSchema.type; + return (Schema) schemaClass.getDeclaredConstructor().newInstance(); + } + + public static class CustomSchema { + public Class type; + public String name; + public String example; + + public CustomSchema(Class type, String name, String example) { + this.type = type; + this.name = name; + this.example = example; + } + + @Override + public String toString() { + return name + "-" + type + "-" + example; + } + } + + // A#$ -> A $ + // A#$#c -> A $ c + // C -> C + // B#C -> B C + public static List getCustomSchemasFromSingleTypeInfo(SingleTypeInfo singleTypeInfo) throws Exception { + String param = singleTypeInfo.getParam(); + List customSchemas = new ArrayList<>(); + String[] paramList = param.split("#"); + int idx = 0; + for (String x: paramList) { + if (idx == paramList.length-1) { + String name = x; + if (Objects.equals(name, "$")) { + name = null; + } + CappedSet values = singleTypeInfo.getValues(); + String example = values != null && values.count() > 0 ? (String) values.getElements().toArray()[0] : null; + customSchemas.add(customSchemaFromSubType(singleTypeInfo.getSubType(), name, example)); + break; + } + if (!Objects.equals(x, "$")) { + if (Objects.equals(paramList[idx + 1], "$")) { + customSchemas.add(new CustomSchema(ArraySchema.class, x, null)); + } else { + customSchemas.add(new CustomSchema(ObjectSchema.class, x, null)); + } + } else { + if (Objects.equals(paramList[idx + 1], "$")) { + customSchemas.add(new CustomSchema(ArraySchema.class, x, null)); + } else { + customSchemas.add(new CustomSchema(ObjectSchema.class, null, null)); + } + } + + idx += 1; + } + + + return customSchemas; + } + + public static CustomSchema customSchemaFromSubType(SingleTypeInfo.SubType subType, String name, String example) { + Class type = subType.getSwaggerSchemaClass(); + + return new CustomSchema(type,name, example); + } + +} diff --git a/libs/utils/src/main/java/com/akto/store/StandardHeaders.java b/libs/utils/src/main/java/com/akto/store/StandardHeaders.java new file mode 100644 index 0000000000..fa5834783c --- /dev/null +++ b/libs/utils/src/main/java/com/akto/store/StandardHeaders.java @@ -0,0 +1,90 @@ +package com.akto.store; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class StandardHeaders { + public static Set headers = new HashSet<>(); + + private static void add(String value) { + if (value == null) return; + String cleanedValue = value.toLowerCase(); + cleanedValue = cleanedValue.trim(); + headers.add(cleanedValue); + } + + public static boolean isStandardHeader(String value) { + String cleanedValue = value.toLowerCase(); + cleanedValue = cleanedValue.trim(); + return headers.contains(cleanedValue); + } + + static { + add("A-IM"); + add("Accept"); + add("Accept-Charset"); + add("Accept-Datetime"); + add("Accept-Encoding"); + add("Accept-Language"); + add("Access-Control-Request-Method"); + add("Access-Control-Request-Headers"); + add("Authorization"); + add("Cache-Control"); + add("Connection"); + add("Content-Encoding"); + add("Content-Length"); + add("Content-MD5"); + add("Content-Type"); + add("Cookie"); + add("Date"); + add("Expect"); + add("Forwarded"); + add("From"); + add("Host"); + add("HTTP2-Settings"); + add("If-Match"); + add("If-Modified-Since"); + add("If-None-Match"); + add("If-Range"); + add("If-Unmodified-Since"); + add("Max-Forwards"); + add("Origin"); + add("Pragma"); + add("Prefer"); + add("Proxy-Authorization"); + add("Range"); + add("Referer"); + add("TE"); + add("Trailer"); + add("Transfer-Encoding"); + add("User-Agent"); + add("Upgrade"); + add("Via"); + add("Warning"); + + add("Field name"); + add("Upgrade-Insecure-Requests"); + add("X-Requested-With"); + add("DNT"); + add("X-Forwarded-For"); + add("X-Forwarded-Host"); + add("X-Forwarded-Proto"); + add("Front-End-Https"); + add("X-Http-Method-Override"); + add("X-ATT-DeviceId"); + add("X-Wap-Profile"); + add("Proxy-Connection"); + add("X-UIDH"); + add("X-Csrf-Token"); + add("X-Request-ID"); + add("X-Correlation-ID"); + add("Save-Data"); + + add("sec-ch-ua"); + add("x-amzn-trace-id"); + + add("idempotency-key"); + + } +} diff --git a/libs/utils/src/main/java/com/akto/task/Cluster.java b/libs/utils/src/main/java/com/akto/task/Cluster.java new file mode 100644 index 0000000000..cfcabae415 --- /dev/null +++ b/libs/utils/src/main/java/com/akto/task/Cluster.java @@ -0,0 +1,62 @@ +package com.akto.task; + +import java.util.UUID; + +import com.akto.dao.context.Context; +import com.akto.dao.DibsDao; +import com.akto.dto.Dibs; +import com.mongodb.MongoCommandException; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.FindOneAndUpdateOptions; +import com.mongodb.client.model.ReturnDocument; +import com.mongodb.client.model.Updates; + +import org.bson.conversions.Bson; + +public class Cluster { + + public static final String RUNTIME_MERGER = "runtime-merger"; + + public static final String winnerId = UUID.randomUUID().toString(); + + public static boolean callDibs(String prize, int expiryPeriod, int freqInSeconds) { + int now = Context.now(); + int expiryTs = expiryPeriod + now; + Bson setOnInsert = Updates.setOnInsert("_id", prize); + + Bson updates = Updates.combine( + Updates.set("winner", winnerId), + Updates.set("expiryTs", expiryTs), + Updates.set("startTs", now), + Updates.set("freqInSeconds", freqInSeconds), + Updates.set("lastPing", now), + setOnInsert + ); + + FindOneAndUpdateOptions options = new FindOneAndUpdateOptions().upsert(true).returnDocument(ReturnDocument.AFTER); + + Dibs dibs; + Bson findKeyQ = Filters.and( + Filters.eq("_id", prize), + Filters.exists("expiryTs", false) + ); + + try { + dibs = DibsDao.instance.getMCollection().findOneAndUpdate(findKeyQ, updates, options); + } catch (MongoCommandException e) { + // already present + Bson findExpiredKeyQ = Filters.and( + Filters.eq("_id", prize), + Filters.lte("expiryTs", now - expiryPeriod) + ); + + try { + dibs = DibsDao.instance.getMCollection().findOneAndUpdate(findExpiredKeyQ, updates, options); + } catch (MongoCommandException eInside) { + dibs = DibsDao.instance.findOne(Filters.eq("_id", prize)); + } + } + return (dibs == null || (dibs.getWinner().equals(winnerId) && dibs.getExpiryTs() == expiryTs)); + } + +} diff --git a/libs/utils/src/main/java/com/akto/task/DataFetcherUtils.java b/libs/utils/src/main/java/com/akto/task/DataFetcherUtils.java new file mode 100644 index 0000000000..cccc7cb0a1 --- /dev/null +++ b/libs/utils/src/main/java/com/akto/task/DataFetcherUtils.java @@ -0,0 +1,15 @@ +package com.akto.task; + +import com.akto.calendar.DateUtils; +import com.akto.dao.context.Context; +import com.akto.dto.*; +import com.mongodb.client.model.Filters; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; + +public class DataFetcherUtils { + + +} diff --git a/libs/utils/src/main/java/com/akto/task/PeriodicTask.java b/libs/utils/src/main/java/com/akto/task/PeriodicTask.java new file mode 100644 index 0000000000..885c940389 --- /dev/null +++ b/libs/utils/src/main/java/com/akto/task/PeriodicTask.java @@ -0,0 +1,27 @@ +package com.akto.task; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +abstract public class PeriodicTask { + + int freqInHours; + + public void start(int freqInHours) { + ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor(); + ses.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + execute(); + } + }, 0, freqInHours, TimeUnit.HOURS); + this.freqInHours = freqInHours; + } + + public int getFreqInHours() { + return freqInHours; + } + + public abstract void execute(); +} diff --git a/libs/utils/src/main/java/com/akto/testing_utils/TestingUtils.java b/libs/utils/src/main/java/com/akto/testing_utils/TestingUtils.java new file mode 100644 index 0000000000..54f0a5f42c --- /dev/null +++ b/libs/utils/src/main/java/com/akto/testing_utils/TestingUtils.java @@ -0,0 +1,53 @@ +package com.akto.testing_utils; + +import com.akto.dao.testing.sources.TestSourceConfigsDao; +import com.akto.dto.test_run_findings.TestingIssuesId; +import com.akto.dto.testing.TestingRunResult; +import com.akto.dto.testing.sources.TestSourceConfig; +import com.akto.util.enums.GlobalEnums; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TestingUtils { + //Private constructor so that it's just a utility class + private TestingUtils() { + } + + + + private static boolean doesExists(List idList, TestingIssuesId issueId) { + for (TestingIssuesId issue : idList) { + if (issue.equals(issueId)) { + return true; + } + } + return false; + } + + public static Map listOfIssuesIdsFromTestingRunResults(List testingRunResults, + boolean isAutomatedTesting) { + + HashMap mapOfIssueIdsvsTestingRunResult = new HashMap<>(); + List idList = new ArrayList<>(); + testingRunResults.forEach(runResult -> { + String subType = runResult.getTestSubType(); + TestSourceConfig config = null; + GlobalEnums.TestSubCategory subCategory = GlobalEnums.TestSubCategory.getTestCategory(subType); + if (subCategory == null) {//Issue came from custom template + config = TestSourceConfigsDao.instance.getTestSourceConfig(subType); + } + TestingIssuesId issueId = new TestingIssuesId(runResult.getApiInfoKey(), + isAutomatedTesting ? + GlobalEnums.TestErrorSource.AUTOMATED_TESTING : GlobalEnums.TestErrorSource.RUNTIME, + subCategory, config != null ?config.getId() : null); + if (!doesExists(idList, issueId)) { + idList.add(issueId); + mapOfIssueIdsvsTestingRunResult.put(issueId, runResult); + } + }); + return mapOfIssueIdsvsTestingRunResult; + } +} diff --git a/libs/utils/src/main/java/com/akto/util/http_request/CustomHttpRequest.java b/libs/utils/src/main/java/com/akto/util/http_request/CustomHttpRequest.java new file mode 100644 index 0000000000..7eee4172dd --- /dev/null +++ b/libs/utils/src/main/java/com/akto/util/http_request/CustomHttpRequest.java @@ -0,0 +1,82 @@ +package com.akto.util.http_request; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.*; +import org.apache.http.client.HttpClient; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.HttpClients; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.Map; + +public class CustomHttpRequest { + private static final HttpClient httpclient = HttpClients.createDefault(); + private static final ObjectMapper mapper = new ObjectMapper(); + + public static Map getRequest(String url, String authHeader) throws HttpResponseException { + HttpGet httpGet = new HttpGet(url); + + httpGet.setHeader(HttpHeaders.CONTENT_TYPE,"application/json"); + httpGet.setHeader(HttpHeaders.AUTHORIZATION,authHeader); + + return s(httpGet); + } + + public static Map postRequest(String url, List params) throws HttpResponseException { + HttpPost httpPost = new HttpPost(url); + + try { + httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); + } catch (UnsupportedEncodingException e) { + ; + } + + return s(httpPost); + + } + + public static Map s(HttpUriRequest request) throws HttpResponseException { + //Execute and get the response. + HttpResponse response = null; + try { + response = httpclient.execute(request); + } catch (IOException ioException) { + } + + if (response == null) { + return null; + } + + HttpEntity entity = response.getEntity(); + StatusLine statusLine = response.getStatusLine(); + if (statusLine.getStatusCode() != 200) { + throw new HttpResponseException(statusLine.getStatusCode(),statusLine.getReasonPhrase()); + } + + InputStream inputStream = null; + Map jsonMap = null; + if (entity != null) { + try { + inputStream = entity.getContent(); + jsonMap = mapper.readValue(inputStream, Map.class); + } catch (IOException ioException) { + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ioException) { + } + } + } + } + + return jsonMap; + } +} diff --git a/libs/utils/src/test/java/com/akto/MongoBasedTest.java b/libs/utils/src/test/java/com/akto/MongoBasedTest.java new file mode 100644 index 0000000000..866eef45f3 --- /dev/null +++ b/libs/utils/src/test/java/com/akto/MongoBasedTest.java @@ -0,0 +1,52 @@ +package com.akto; + +import com.akto.dao.context.Context; +import com.mongodb.ConnectionString; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +import de.flapdoodle.embed.mongo.MongodExecutable; +import de.flapdoodle.embed.mongo.MongodProcess; +import de.flapdoodle.embed.mongo.MongodStarter; +import de.flapdoodle.embed.mongo.config.ImmutableMongodConfig; +import de.flapdoodle.embed.mongo.config.Net; +import de.flapdoodle.embed.mongo.distribution.Version; +import org.junit.jupiter.api.BeforeEach; + +public class MongoBasedTest { + + public static final int ACCOUNT_ID = 12389; + + public static MongodExecutable mongodExe; + public static MongodProcess mongod; + + @BeforeEach + + @BeforeClass + public static void beforeClass() throws Exception { + MongodStarter starter = MongodStarter.getDefaultInstance(); + String bindIp = "localhost"; + ImmutableMongodConfig mongodConfig = ImmutableMongodConfig.builder() + .version(Version.Main.PRODUCTION) + .net(new Net(bindIp, 27019, false)) + .build(); + mongodExe = starter.prepare(mongodConfig); + mongod = mongodExe.start(); + DaoInit.init(new ConnectionString("mongodb://localhost:27019")); + Context.accountId.set(ACCOUNT_ID); + } + + @AfterClass + public static void afterClass() throws Exception { + if (mongod != null) { + mongod.stop(); + mongodExe.stop(); + } + } + + + +} diff --git a/libs/utils/src/test/java/com/akto/calendar/TestDateUtils.java b/libs/utils/src/test/java/com/akto/calendar/TestDateUtils.java new file mode 100644 index 0000000000..daafee337b --- /dev/null +++ b/libs/utils/src/test/java/com/akto/calendar/TestDateUtils.java @@ -0,0 +1,32 @@ +package com.akto.calendar; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.Test; + +import com.akto.dao.context.Context; + +public class TestDateUtils { + + @Test + public void testPrettifyDelta() { + String result = DateUtils.prettifyDelta(Context.now() - 20); + assertEquals("1 minute ago", result); + + result = DateUtils.prettifyDelta(Context.now() - 200); + assertEquals("3 minutes ago", result); + + result = DateUtils.prettifyDelta(Context.now() - 3000); + assertEquals("50 minutes ago", result); + + result = DateUtils.prettifyDelta(Context.now()- 3700); + assertEquals("1 hour ago", result); + + result = DateUtils.prettifyDelta(Context.now()-7400); + assertEquals("2 hours ago", result); + + result = DateUtils.prettifyDelta(Context.now()-94000); + assertEquals("couple of days ago", result); + } + +} diff --git a/libs/utils/src/test/java/com/akto/open_api/TestAddPathItems.java b/libs/utils/src/test/java/com/akto/open_api/TestAddPathItems.java new file mode 100644 index 0000000000..ea4f50ac87 --- /dev/null +++ b/libs/utils/src/test/java/com/akto/open_api/TestAddPathItems.java @@ -0,0 +1,46 @@ +package com.akto.open_api; + +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import org.junit.Test; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestAddPathItems { + + @Test + public void happy() { + Paths paths = new Paths(); + try { + Main.addPathItems(200,paths, "/api/1", "GET", new ArrayList<>(), true); + Main.addPathItems(404,paths, "/api/1", "GET", new ArrayList<>(), true); + Main.addPathItems(200,paths, "/api/1", "POST", new ArrayList<>(), true); + Main.addPathItems(200,paths, "/api/1", "PUT", new ArrayList<>(), true); + Main.addPathItems(200,paths, "/api/2", "GET", new ArrayList<>(), true); + } catch (Exception e) { + ; + } + assertEquals(paths.size(), 2); + PathItem pathItem1 = paths.get("/api/1"); + PathItem pathItem2 = paths.get("/api/2"); + assertNotNull(pathItem1); + assertNotNull(pathItem2); + + assertNotNull(pathItem1.getGet()); + assertNotNull(pathItem1.getPost()); + assertNotNull(pathItem1.getPut()); + assertNull(pathItem1.getDelete()); + + Operation getOperation = pathItem1.getGet(); + assertEquals(getOperation.getResponses().size(),2); + assertNotNull(getOperation.getResponses().get("200")); + assertNotNull(getOperation.getResponses().get("404")); + assertNull(getOperation.getResponses().get("444")); + + Operation putOperation = pathItem1.getPut(); + assertEquals(putOperation.getResponses().size(),1); + } +} diff --git a/libs/utils/src/test/java/com/akto/open_api/TestCustomSchemasFromSingleTypeInfo.java b/libs/utils/src/test/java/com/akto/open_api/TestCustomSchemasFromSingleTypeInfo.java new file mode 100644 index 0000000000..6d485ad665 --- /dev/null +++ b/libs/utils/src/test/java/com/akto/open_api/TestCustomSchemasFromSingleTypeInfo.java @@ -0,0 +1,134 @@ +package com.akto.open_api; + +import com.akto.dto.type.SingleTypeInfo; +import com.akto.types.CappedSet; +import io.swagger.v3.oas.models.media.*; +import org.junit.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.HashSet; +import java.util.List; + +public class TestCustomSchemasFromSingleTypeInfo { + + public static SingleTypeInfo generateSingleTypeInfo(String param, SingleTypeInfo.SubType subType) { + SingleTypeInfo.ParamId p = new SingleTypeInfo.ParamId("/api","GET",200,false,param,subType,0, false); + return new SingleTypeInfo(p,new HashSet<>(),new HashSet<>(),0,0,0, new CappedSet<>(), SingleTypeInfo.Domain.ENUM, SingleTypeInfo.ACCEPTED_MAX_VALUE, SingleTypeInfo.ACCEPTED_MIN_VALUE); + } + @Test + public void testSimple() throws Exception { + List customSchemaList; + + SingleTypeInfo s = generateSingleTypeInfo("id", SingleTypeInfo.GENERIC); + customSchemaList = SchemaBuilder.getCustomSchemasFromSingleTypeInfo(s); + assertEquals(1, customSchemaList.size()); + assertEquals("id", customSchemaList.get(0).name); + assertEquals(StringSchema.class, customSchemaList.get(0).type); + + } + + @Test + public void testSimpleObject() throws Exception { + List customSchemaList; + + SingleTypeInfo s = generateSingleTypeInfo("user#name#first", SingleTypeInfo.GENERIC); + customSchemaList = SchemaBuilder.getCustomSchemasFromSingleTypeInfo(s); + assertEquals(3, customSchemaList.size()); + assertEquals("user", customSchemaList.get(0).name); + assertEquals(ObjectSchema.class, customSchemaList.get(0).type); + assertEquals("name", customSchemaList.get(1).name); + assertEquals(ObjectSchema.class, customSchemaList.get(1).type); + assertEquals("first", customSchemaList.get(2).name); + assertEquals(StringSchema.class, customSchemaList.get(2).type); + } + + @Test + public void testObjectInArray() throws Exception { + List customSchemaList; + + SingleTypeInfo s = generateSingleTypeInfo("cards#$#id", SingleTypeInfo.INTEGER_32); + customSchemaList = SchemaBuilder.getCustomSchemasFromSingleTypeInfo(s); + assertEquals(3, customSchemaList.size()); + assertEquals("cards", customSchemaList.get(0).name); + assertEquals(ArraySchema.class, customSchemaList.get(0).type); + assertNull(customSchemaList.get(1).name); + assertEquals(ObjectSchema.class, customSchemaList.get(1).type); + assertEquals("id", customSchemaList.get(2).name); + assertEquals(IntegerSchema.class, customSchemaList.get(2).type); + } + + @Test + public void testNestedObjectInArray() throws Exception { + List customSchemaList; + + SingleTypeInfo s = generateSingleTypeInfo("cards#$#user#name", SingleTypeInfo.GENERIC); + customSchemaList = SchemaBuilder.getCustomSchemasFromSingleTypeInfo(s); + assertEquals(4, customSchemaList.size()); + assertEquals("cards", customSchemaList.get(0).name); + assertEquals(ArraySchema.class, customSchemaList.get(0).type); + assertNull(customSchemaList.get(1).name); + assertEquals(ObjectSchema.class, customSchemaList.get(1).type); + assertEquals("user", customSchemaList.get(2).name); + assertEquals(ObjectSchema.class, customSchemaList.get(2).type); + assertEquals("name", customSchemaList.get(3).name); + assertEquals(StringSchema.class, customSchemaList.get(3).type); + } + + @Test + public void testIntegerArray() throws Exception { + List customSchemaList; + + SingleTypeInfo s = generateSingleTypeInfo("cards#$", SingleTypeInfo.INTEGER_32); + customSchemaList = SchemaBuilder.getCustomSchemasFromSingleTypeInfo(s); + assertEquals(2, customSchemaList.size()); + assertEquals("cards", customSchemaList.get(0).name); + assertEquals(ArraySchema.class, customSchemaList.get(0).type); + assertNull(customSchemaList.get(1).name); + assertEquals(IntegerSchema.class, customSchemaList.get(1).type); + } + + @Test + public void testBareIntegerArray() throws Exception { + List customSchemaList; + + SingleTypeInfo s = generateSingleTypeInfo("json#$#id", SingleTypeInfo.FLOAT); + customSchemaList = SchemaBuilder.getCustomSchemasFromSingleTypeInfo(s); + assertEquals(3, customSchemaList.size()); + assertEquals("json", customSchemaList.get(0).name); + assertEquals(ArraySchema.class, customSchemaList.get(0).type); + assertNull(customSchemaList.get(1).name); + assertEquals(ObjectSchema.class, customSchemaList.get(1).type); + assertEquals("id", customSchemaList.get(2).name); + assertEquals(NumberSchema.class, customSchemaList.get(2).type); + } + + @Test + public void testArrayInArray() throws Exception { + List customSchemaList; + + SingleTypeInfo s = generateSingleTypeInfo("data#apiInfoList#$#allAuthTypesFound#$#$", SingleTypeInfo.GENERIC); + customSchemaList = SchemaBuilder.getCustomSchemasFromSingleTypeInfo(s); + + assertEquals(6, customSchemaList.size()); + + assertEquals("data", customSchemaList.get(0).name); + assertEquals(ObjectSchema.class, customSchemaList.get(0).type); + + assertEquals("apiInfoList", customSchemaList.get(1).name); + assertEquals(ArraySchema.class, customSchemaList.get(1).type); + + assertEquals(ObjectSchema.class, customSchemaList.get(2).type); + + assertEquals("allAuthTypesFound", customSchemaList.get(3).name); + assertEquals(ArraySchema.class, customSchemaList.get(3).type); + + // assertEquals("allAuthTypesFound", customSchemaList.get(2).name); + assertEquals(ArraySchema.class, customSchemaList.get(4).type); + + assertEquals(null, customSchemaList.get(5).name); + assertEquals(StringSchema.class, customSchemaList.get(5).type); + } + +} diff --git a/libs/utils/src/test/java/com/akto/open_api/TestPathBuilder.java b/libs/utils/src/test/java/com/akto/open_api/TestPathBuilder.java new file mode 100644 index 0000000000..2dc67d72be --- /dev/null +++ b/libs/utils/src/test/java/com/akto/open_api/TestPathBuilder.java @@ -0,0 +1,50 @@ +package com.akto.open_api; + +import io.swagger.models.parameters.Parameter; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.media.IntegerSchema; +import io.swagger.v3.oas.models.media.StringSchema; +import org.junit.Test; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; +import java.util.List; + +public class TestPathBuilder { + + @Test + public void testFixPathBaseCase() { + Paths paths = new Paths(); + PathItem pathItem = new PathItem(); + String oldUrl = "/api/books/integer/author/string/something/ii"; + paths.addPathItem(oldUrl, pathItem); + + paths = PathBuilder.parameterizePath(paths); + + PathItem newPathItem = paths.get(oldUrl); + assertNotNull(newPathItem); + assertEquals(newPathItem.getParameters().size(), 0); + } + + @Test + public void testFixPathMultipleParams() { + Paths paths = new Paths(); + PathItem pathItem = new PathItem(); + String oldUrl = "/api/books/INTEGER/author/STRING/string/INTEGER"; + paths.addPathItem(oldUrl, pathItem); + + paths = PathBuilder.parameterizePath(paths); + + String newUrl = "/api/books/{param1}/author/{param2}/string/{param3}"; + PathItem newPathItem = paths.get(newUrl); + assertNotNull(newPathItem); + assertNull(paths.get(oldUrl)); + assertEquals(newPathItem.getParameters().size(),3); + assertEquals(newPathItem.getParameters().get(0).getSchema().getClass(), IntegerSchema.class); + assertEquals(newPathItem.getParameters().get(1).getSchema().getClass(), StringSchema.class); + assertEquals(newPathItem.getParameters().get(2).getSchema().getClass(), IntegerSchema.class); + + } +} diff --git a/libs/utils/src/test/java/com/akto/open_api/TestSchemaBuilder.java b/libs/utils/src/test/java/com/akto/open_api/TestSchemaBuilder.java new file mode 100644 index 0000000000..cc7dc3c878 --- /dev/null +++ b/libs/utils/src/test/java/com/akto/open_api/TestSchemaBuilder.java @@ -0,0 +1,91 @@ +package com.akto.open_api; + +import com.akto.dto.type.SingleTypeInfo; +import io.swagger.v3.oas.models.media.*; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static com.akto.open_api.TestCustomSchemasFromSingleTypeInfo.generateSingleTypeInfo; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class TestSchemaBuilder { + + @Test + public void testComplexParams() throws Exception{ + SingleTypeInfo s1 = generateSingleTypeInfo("accessToken", SingleTypeInfo.JWT); + SingleTypeInfo s2 = generateSingleTypeInfo("ip", SingleTypeInfo.IP_ADDRESS); + List singleTypeInfoList = Arrays.asList(s1,s2); + + Schema schema = Main.buildSchema(singleTypeInfoList); + assertEquals(schema.getClass(), ObjectSchema.class); + //noinspection rawtypes + Map propertiesMap = schema.getProperties(); + + assertEquals(propertiesMap.get("accessToken").getClass(), StringSchema.class); + assertEquals(propertiesMap.get("ip").getClass(), StringSchema.class); + } + + @Test + public void testSimpleObject() throws Exception { + SingleTypeInfo s1 = generateSingleTypeInfo("user#name#first", SingleTypeInfo.GENERIC); + SingleTypeInfo s2 = generateSingleTypeInfo("user#name#last", SingleTypeInfo.GENERIC); + SingleTypeInfo s3 = generateSingleTypeInfo("user#age", SingleTypeInfo.INTEGER_32); + SingleTypeInfo s4 = generateSingleTypeInfo("id", SingleTypeInfo.INTEGER_64); + SingleTypeInfo s5 = generateSingleTypeInfo("email", SingleTypeInfo.EMAIL); + SingleTypeInfo s6 = generateSingleTypeInfo("cards#$#id", SingleTypeInfo.GENERIC); + SingleTypeInfo s7 = generateSingleTypeInfo("cards#$#name", SingleTypeInfo.GENERIC); + SingleTypeInfo s8 = generateSingleTypeInfo("cards#$#dashboard#$#dashboard_id", SingleTypeInfo.UUID); + SingleTypeInfo s9 = generateSingleTypeInfo("cards#$#dashboard#$#name", SingleTypeInfo.GENERIC); + SingleTypeInfo s10 = generateSingleTypeInfo("cards#$#dashboard#$#time", SingleTypeInfo.INTEGER_64); + SingleTypeInfo s11 = generateSingleTypeInfo("prices#$", SingleTypeInfo.FLOAT); + List singleTypeInfoList = Arrays.asList(s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11); + + Schema schema = Main.buildSchema(singleTypeInfoList); + assertEquals(schema.getClass(), ObjectSchema.class); + + //noinspection rawtypes + Map propertiesMap = schema.getProperties(); + + assertEquals(propertiesMap.get("user").getClass(),ObjectSchema.class); + assertEquals(propertiesMap.get("id").getClass(), IntegerSchema.class); + assertEquals(propertiesMap.get("email").getClass(), EmailSchema.class); + assertEquals(propertiesMap.get("cards").getClass(), ArraySchema.class); + assertEquals(propertiesMap.get("prices").getClass(), ArraySchema.class); + + ObjectSchema userSchema = (ObjectSchema) propertiesMap.get("user"); + //noinspection rawtypes + Map userPropertiesMap = userSchema.getProperties(); + assertEquals(userPropertiesMap.get("name").getClass(), ObjectSchema.class); + assertEquals(userPropertiesMap.get("age").getClass(), IntegerSchema.class); + + ObjectSchema userNameSchema = (ObjectSchema) userPropertiesMap.get("name"); + //noinspection rawtypes + Map userNamePropertiesMap = userNameSchema.getProperties(); + assertEquals(userNamePropertiesMap.get("first").getClass(), StringSchema.class); + assertEquals(userNamePropertiesMap.get("last").getClass(), StringSchema.class); + + ArraySchema cardsSchema = (ArraySchema) propertiesMap.get("cards"); + Schema cardItems = cardsSchema.getItems(); + assertEquals(cardItems.getClass(), ObjectSchema.class); + + //noinspection rawtypes + Map cardItemPropertiesMap = cardItems.getProperties(); + assertEquals(cardItemPropertiesMap.get("dashboard").getClass(), ArraySchema.class); + + ArraySchema dashboardSchema = (ArraySchema) cardItemPropertiesMap.get("dashboard"); + Schema dashboardItems = dashboardSchema.getItems(); + assertEquals(dashboardItems.getClass(), ObjectSchema.class); + + //noinspection rawtypes + Map dashboardItemsPropertiesMap = dashboardItems.getProperties(); + assertEquals(dashboardItemsPropertiesMap.get("name").getClass(), StringSchema.class); + assertEquals(dashboardItemsPropertiesMap.get("time").getClass(), IntegerSchema.class); + + Schema pricesItems = ((ArraySchema) propertiesMap.get("prices")).getItems(); + assertEquals(pricesItems.getClass(), NumberSchema.class); + } +} diff --git a/libs/utils/src/test/java/com/akto/utils/DaoConnect.java b/libs/utils/src/test/java/com/akto/utils/DaoConnect.java new file mode 100644 index 0000000000..ab02a75c94 --- /dev/null +++ b/libs/utils/src/test/java/com/akto/utils/DaoConnect.java @@ -0,0 +1,18 @@ +package com.akto.utils; + +import com.akto.DaoInit; +import com.akto.dao.context.Context; +import com.mongodb.ConnectionString; +import org.junit.BeforeClass; + +public class DaoConnect { + + static String mongoURI = "mongodb://write_ops:write_ops@cluster0-shard-00-00.yg43a.mongodb.net:27017,cluster0-shard-00-01.yg43a.mongodb.net:27017,cluster0-shard-00-02.yg43a.mongodb.net:27017/myFirstDatabase?ssl=true&replicaSet=atlas-qd3mle-shard-0&authSource=admin&retryWrites=true&w=majority"; + + @BeforeClass + public static void setup() { + ConnectionString connectionString = new ConnectionString(mongoURI); + DaoInit.init(connectionString); + Context.accountId.set(1000000000); + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..b92602f139 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,27 @@ +{ + "name": "mono", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "uuid": "^9.0.0" + } + }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + } + }, + "dependencies": { + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + } + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000..7343be7f98 --- /dev/null +++ b/pom.xml @@ -0,0 +1,116 @@ + + + + 4.0.0 + + com.akto + root + ${revision} + pom + + + 1.0-SNAPSHOT + UTF-8 + 1.8 + 1.8 + akto-api-security + https://sonarcloud.io + 0.8.5 + ${project.basedir}/../../target/jacoco.exec + + + + apps + libs + + + + + + + org.codehaus.mojo + flatten-maven-plugin + 1.1.0 + + ${project.build.directory} + + + + + flatten + process-resources + + flatten + + + + + flatten.clean + clean + + clean + + + + + + org.eclipse.jetty + jetty-maven-plugin + 9.4.36.v20210114 + + web + + / + WEB-INF/web.xml + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + prepare-agent + + prepare-agent + + + + report + prepare-package + + report + + + + post-unit-test + test + + report + + + + + + Akto.jar + + ${jacoco.reportPath} + ${jacoco.reportPath} + true + + + + + + + + junit + junit + 4.13.1 + test + + + +